2022-10-08 22:52:18 +02:00
|
|
|
/* RetroArch - A frontend for libretro.
|
|
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
|
|
* Copyright (C) 2011-2021 - 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/>.
|
|
|
|
*/
|
|
|
|
|
2023-08-15 16:02:12 +02:00
|
|
|
#include <stdlib.h>
|
2022-10-08 22:52:18 +02:00
|
|
|
#include <math.h>
|
2023-08-15 16:02:12 +02:00
|
|
|
|
|
|
|
#include <retro_inline.h>
|
2022-10-08 22:52:18 +02:00
|
|
|
#include <string/stdstring.h>
|
|
|
|
#include <retro_math.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "../config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <gfx/video_frame.h>
|
|
|
|
|
|
|
|
#include "../config.def.h"
|
|
|
|
|
|
|
|
#include "video_driver.h"
|
|
|
|
#include "video_filter.h"
|
|
|
|
#include "video_display_server.h"
|
|
|
|
|
|
|
|
#include "gfx_animation.h"
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
|
|
#include "gfx_widgets.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
#include "video_thread_wrapper.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_MENU
|
|
|
|
#include "../menu/menu_driver.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include "common/win32_common.h"
|
|
|
|
#endif
|
|
|
|
|
2023-06-17 22:43:05 -04:00
|
|
|
#ifdef __WINRT__
|
|
|
|
#include "../uwp/uwp_func.h"
|
|
|
|
#endif
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
#include "../audio/audio_driver.h"
|
|
|
|
#include "../frontend/frontend_driver.h"
|
|
|
|
#include "../record/record_driver.h"
|
|
|
|
#include "../ui/ui_companion_driver.h"
|
|
|
|
#include "../driver.h"
|
|
|
|
#include "../file_path_special.h"
|
|
|
|
#include "../list_special.h"
|
|
|
|
#include "../retroarch.h"
|
|
|
|
#include "../verbosity.h"
|
|
|
|
|
|
|
|
#define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time)))
|
|
|
|
|
2023-05-31 01:09:38 +02:00
|
|
|
#define FRAME_DELAY_AUTO_DEBUG 0
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
struct string_list *list;
|
|
|
|
enum gfx_ctx_api api;
|
|
|
|
} gfx_api_gpu_map;
|
|
|
|
|
|
|
|
static gfx_api_gpu_map gpu_map[] = {
|
|
|
|
{ NULL, GFX_CTX_VULKAN_API },
|
|
|
|
{ NULL, GFX_CTX_DIRECT3D10_API },
|
|
|
|
{ NULL, GFX_CTX_DIRECT3D11_API },
|
|
|
|
{ NULL, GFX_CTX_DIRECT3D12_API }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const video_display_server_t dispserv_null = {
|
|
|
|
NULL, /* init */
|
|
|
|
NULL, /* destroy */
|
|
|
|
NULL, /* set_window_opacity */
|
|
|
|
NULL, /* set_window_progress */
|
|
|
|
NULL, /* set_window_decorations */
|
|
|
|
NULL, /* set_resolution */
|
|
|
|
NULL, /* get_resolution_list */
|
|
|
|
NULL, /* get_output_options */
|
|
|
|
NULL, /* set_screen_orientation */
|
|
|
|
NULL, /* get_screen_orientation */
|
|
|
|
NULL, /* get_flags */
|
|
|
|
"null"
|
|
|
|
};
|
|
|
|
|
|
|
|
static const gfx_ctx_driver_t *gfx_ctx_gl_drivers[] = {
|
|
|
|
#if defined(ORBIS)
|
|
|
|
&orbis_ctx,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_VITAGL) | defined(HAVE_VITAGLES)
|
|
|
|
&vita_ctx,
|
|
|
|
#endif
|
2023-02-09 23:29:45 -08:00
|
|
|
#if defined(__PS3__)
|
2022-10-08 22:52:18 +02:00
|
|
|
&gfx_ctx_ps3,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_LIBNX) && defined(HAVE_OPENGL)
|
|
|
|
&switch_ctx,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_VIDEOCORE)
|
|
|
|
&gfx_ctx_videocore,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_MALI_FBDEV)
|
|
|
|
&gfx_ctx_mali_fbdev,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_VIVANTE_FBDEV)
|
|
|
|
&gfx_ctx_vivante_fbdev,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_OPENDINGUX_FBDEV)
|
|
|
|
&gfx_ctx_opendingux_fbdev,
|
|
|
|
#endif
|
|
|
|
#if defined(_WIN32) && !defined(__WINRT__) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE))
|
|
|
|
&gfx_ctx_wgl,
|
|
|
|
#endif
|
|
|
|
#if defined(__WINRT__) && defined(HAVE_OPENGLES)
|
|
|
|
&gfx_ctx_uwp,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_WAYLAND)
|
|
|
|
&gfx_ctx_wayland,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_X11) && !defined(HAVE_OPENGLES)
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
|
|
|
|
&gfx_ctx_x,
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_X11) && defined(HAVE_OPENGL) && defined(HAVE_EGL)
|
|
|
|
&gfx_ctx_x_egl,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_KMS)
|
|
|
|
#if defined(HAVE_ODROIDGO2)
|
|
|
|
&gfx_ctx_go2_drm,
|
|
|
|
#endif
|
|
|
|
&gfx_ctx_drm,
|
|
|
|
#endif
|
|
|
|
#if defined(ANDROID)
|
|
|
|
&gfx_ctx_android,
|
|
|
|
#endif
|
|
|
|
#if defined(__QNX__)
|
|
|
|
&gfx_ctx_qnx,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_COCOA) || defined(HAVE_COCOATOUCH) || defined(HAVE_COCOA_METAL)
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
|
|
&gfx_ctx_cocoagl,
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if (defined(HAVE_SDL) || defined(HAVE_SDL2)) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE))
|
|
|
|
&gfx_ctx_sdl_gl,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_OSMESA
|
|
|
|
&gfx_ctx_osmesa,
|
|
|
|
#endif
|
|
|
|
#ifdef EMSCRIPTEN
|
|
|
|
&gfx_ctx_emscripten,
|
|
|
|
#endif
|
|
|
|
&gfx_ctx_null,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2022-12-19 21:36:06 +01:00
|
|
|
/* Beware when changing this - it must match with enum aspect_ratio order */
|
2022-10-08 22:52:18 +02:00
|
|
|
struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = {
|
|
|
|
{ 4.0f / 3.0f , "4:3" },
|
|
|
|
{ 16.0f / 9.0f , "16:9" },
|
|
|
|
{ 16.0f / 10.0f, "16:10" },
|
|
|
|
{ 16.0f / 15.0f, "16:15" },
|
|
|
|
{ 21.0f / 9.0f , "21:9" },
|
|
|
|
{ 1.0f / 1.0f , "1:1" },
|
|
|
|
{ 2.0f / 1.0f , "2:1" },
|
|
|
|
{ 3.0f / 2.0f , "3:2" },
|
|
|
|
{ 3.0f / 4.0f , "3:4" },
|
|
|
|
{ 4.0f / 1.0f , "4:1" },
|
|
|
|
{ 9.0f / 16.0f , "9:16" },
|
|
|
|
{ 5.0f / 4.0f , "5:4" },
|
|
|
|
{ 6.0f / 5.0f , "6:5" },
|
|
|
|
{ 7.0f / 9.0f , "7:9" },
|
|
|
|
{ 8.0f / 3.0f , "8:3" },
|
|
|
|
{ 8.0f / 7.0f , "8:7" },
|
|
|
|
{ 19.0f / 12.0f, "19:12" },
|
|
|
|
{ 19.0f / 14.0f, "19:14" },
|
|
|
|
{ 30.0f / 17.0f, "30:17" },
|
|
|
|
{ 32.0f / 9.0f , "32:9" },
|
2022-12-19 21:36:06 +01:00
|
|
|
{ 0.0f , "" }, /* config - initialized in video_driver_init_internal */
|
|
|
|
{ 1.0f , "" }, /* square pixel - initialized in video_driver_set_viewport_square_pixel */
|
|
|
|
{ 1.0f , "" }, /* core provided - initialized in video_driver_init_internal */
|
|
|
|
{ 0.0f , "" }, /* custom - initialized in video_driver_init_internal */
|
|
|
|
{ 4.0f / 3.0f , "" } /* full - initialized in video_driver_init_internal */
|
2022-10-08 22:52:18 +02:00
|
|
|
};
|
|
|
|
|
2023-08-15 16:02:12 +02:00
|
|
|
static INLINE bool realloc_checked(void **ptr, size_t size)
|
|
|
|
{
|
|
|
|
void *nptr = NULL;
|
|
|
|
|
|
|
|
if (*ptr)
|
|
|
|
nptr = realloc(*ptr, size);
|
|
|
|
else
|
|
|
|
nptr = malloc(size);
|
|
|
|
|
|
|
|
if (nptr)
|
|
|
|
*ptr = nptr;
|
|
|
|
|
|
|
|
return *ptr == nptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool video_coord_array_resize(video_coord_array_t *ca,
|
|
|
|
unsigned cap)
|
|
|
|
{
|
|
|
|
size_t base_size = sizeof(float) * cap;
|
|
|
|
|
|
|
|
if (!realloc_checked((void**)&ca->coords.vertex,
|
|
|
|
2 * base_size))
|
|
|
|
return false;
|
|
|
|
if (!realloc_checked((void**)&ca->coords.color,
|
|
|
|
4 * base_size))
|
|
|
|
return false;
|
|
|
|
if (!realloc_checked((void**)&ca->coords.tex_coord,
|
|
|
|
2 * base_size))
|
|
|
|
return false;
|
|
|
|
if (!realloc_checked((void**)&ca->coords.lut_tex_coord,
|
|
|
|
2 * base_size))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ca->allocated = cap;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_coord_array_append(video_coord_array_t *ca,
|
|
|
|
const video_coords_t *coords, unsigned count)
|
|
|
|
{
|
|
|
|
size_t base_size, offset;
|
|
|
|
count = MIN(count, coords->vertices);
|
|
|
|
|
|
|
|
if (ca->coords.vertices + count >= ca->allocated)
|
|
|
|
{
|
|
|
|
unsigned cap = next_pow2(ca->coords.vertices + count);
|
|
|
|
if (!video_coord_array_resize(ca, cap))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
base_size = count * sizeof(float);
|
|
|
|
offset = ca->coords.vertices;
|
|
|
|
|
|
|
|
/* XXX: I wish we used interlaced arrays so
|
|
|
|
* we could call memcpy only once. */
|
|
|
|
memcpy(ca->coords.vertex + offset * 2,
|
|
|
|
coords->vertex, base_size * 2);
|
|
|
|
|
|
|
|
memcpy(ca->coords.color + offset * 4,
|
|
|
|
coords->color, base_size * 4);
|
|
|
|
|
|
|
|
memcpy(ca->coords.tex_coord + offset * 2,
|
|
|
|
coords->tex_coord, base_size * 2);
|
|
|
|
|
|
|
|
memcpy(ca->coords.lut_tex_coord + offset * 2,
|
|
|
|
coords->lut_tex_coord, base_size * 2);
|
|
|
|
|
|
|
|
ca->coords.vertices += count;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_coord_array_free(video_coord_array_t *ca)
|
|
|
|
{
|
|
|
|
if (!ca->allocated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ca->coords.vertex)
|
|
|
|
free(ca->coords.vertex);
|
|
|
|
ca->coords.vertex = NULL;
|
|
|
|
|
|
|
|
if (ca->coords.color)
|
|
|
|
free(ca->coords.color);
|
|
|
|
ca->coords.color = NULL;
|
|
|
|
|
|
|
|
if (ca->coords.tex_coord)
|
|
|
|
free(ca->coords.tex_coord);
|
|
|
|
ca->coords.tex_coord = NULL;
|
|
|
|
|
|
|
|
if (ca->coords.lut_tex_coord)
|
|
|
|
free(ca->coords.lut_tex_coord);
|
|
|
|
ca->coords.lut_tex_coord = NULL;
|
|
|
|
|
|
|
|
ca->coords.vertices = 0;
|
|
|
|
ca->allocated = 0;
|
|
|
|
}
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
static void *video_null_init(const video_info_t *video,
|
|
|
|
input_driver_t **input, void **input_data)
|
|
|
|
{
|
2022-10-09 10:20:19 +02:00
|
|
|
*input = NULL;
|
2022-10-08 22:52:18 +02:00
|
|
|
*input_data = NULL;
|
|
|
|
|
|
|
|
frontend_driver_install_signal_handler();
|
|
|
|
|
|
|
|
return (void*)-1;
|
|
|
|
}
|
|
|
|
|
2022-10-09 10:25:35 +02:00
|
|
|
static bool video_null_frame(void *a, const void *b, unsigned c, unsigned d,
|
|
|
|
uint64_t e,
|
|
|
|
unsigned f, const char *g, video_frame_info_t *h) { return true; }
|
|
|
|
static void video_null_free(void *a) { }
|
|
|
|
static void video_null_set_nonblock_state(void *a, bool b, bool c, unsigned d) { }
|
|
|
|
static bool video_null_alive(void *a) { return frontend_driver_get_signal_handler_state() != 1; }
|
|
|
|
static bool video_null_focus(void *a) { return true; }
|
|
|
|
static bool video_null_has_windowed(void *a) { return true; }
|
|
|
|
static bool video_null_suppress_screensaver(void *a, bool b) { return false; }
|
|
|
|
static bool video_null_set_shader(void *a, enum rarch_shader_type b, const char
|
|
|
|
*c) { return false; }
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
video_driver_t video_null = {
|
|
|
|
video_null_init,
|
|
|
|
video_null_frame,
|
|
|
|
video_null_set_nonblock_state,
|
|
|
|
video_null_alive,
|
|
|
|
video_null_focus,
|
|
|
|
video_null_suppress_screensaver,
|
|
|
|
video_null_has_windowed,
|
|
|
|
video_null_set_shader,
|
|
|
|
video_null_free,
|
|
|
|
"null",
|
|
|
|
NULL, /* set_viewport */
|
|
|
|
NULL, /* set_rotation */
|
|
|
|
NULL, /* viewport_info */
|
|
|
|
NULL, /* read_viewport */
|
|
|
|
NULL, /* read_frame_raw */
|
|
|
|
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
|
|
NULL, /* overlay_interface */
|
|
|
|
#endif
|
|
|
|
NULL, /* get_poke_interface */
|
|
|
|
};
|
|
|
|
|
|
|
|
const video_driver_t *video_drivers[] = {
|
2023-02-21 14:24:00 +01:00
|
|
|
#ifdef HAVE_GCM
|
2022-10-08 22:52:18 +02:00
|
|
|
&video_gcm,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_VITA2D
|
|
|
|
&video_vita2d,
|
|
|
|
#endif
|
2022-11-20 08:26:34 +02:00
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
&video_vulkan,
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
#if defined(HAVE_OPENGL_CORE)
|
|
|
|
&video_gl3,
|
|
|
|
#endif
|
2022-11-20 08:26:34 +02:00
|
|
|
#ifdef HAVE_OPENGL
|
|
|
|
&video_gl2,
|
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
#ifdef HAVE_OPENGL1
|
|
|
|
&video_gl1,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_METAL
|
|
|
|
&video_metal,
|
|
|
|
#endif
|
|
|
|
#ifdef XENON
|
|
|
|
&video_xenon360,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_D3D12)
|
|
|
|
&video_d3d12,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_D3D11)
|
|
|
|
&video_d3d11,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_D3D10)
|
|
|
|
&video_d3d10,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_D3D9)
|
|
|
|
#if defined(HAVE_HLSL)
|
|
|
|
&video_d3d9_hlsl,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_CG)
|
|
|
|
&video_d3d9_cg,
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_D3D8)
|
|
|
|
&video_d3d8,
|
|
|
|
#endif
|
|
|
|
#ifdef PSP
|
|
|
|
&video_psp1,
|
|
|
|
#endif
|
|
|
|
#ifdef PS2
|
|
|
|
&video_ps2,
|
|
|
|
#endif
|
|
|
|
#ifdef _3DS
|
|
|
|
&video_ctr,
|
|
|
|
#endif
|
|
|
|
#ifdef SWITCH
|
|
|
|
&video_switch,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_ODROIDGO2
|
|
|
|
&video_oga,
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_SDL) && !defined(HAVE_SDL_DINGUX)
|
|
|
|
&video_sdl,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SDL2
|
|
|
|
&video_sdl2,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SDL_DINGUX
|
|
|
|
#if defined(RS90) || defined(MIYOO)
|
|
|
|
&video_sdl_rs90,
|
|
|
|
#else
|
|
|
|
&video_sdl_dingux,
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_XVIDEO
|
|
|
|
&video_xvideo,
|
|
|
|
#endif
|
|
|
|
#ifdef GEKKO
|
|
|
|
&video_gx,
|
|
|
|
#endif
|
|
|
|
#ifdef WIIU
|
|
|
|
&video_wiiu,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_VG
|
|
|
|
&video_vg,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_OMAP
|
|
|
|
&video_omap,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_EXYNOS
|
|
|
|
&video_exynos,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_DISPMANX
|
|
|
|
&video_dispmanx,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SUNXI
|
|
|
|
&video_sunxi,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PLAIN_DRM
|
|
|
|
&video_drm,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_XSHM
|
|
|
|
&video_xshm,
|
|
|
|
#endif
|
|
|
|
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
|
|
|
|
#ifdef HAVE_GDI
|
|
|
|
&video_gdi,
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef DJGPP
|
|
|
|
&video_vga,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_FPGA
|
|
|
|
&video_fpga,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SIXEL
|
|
|
|
&video_sixel,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_CACA
|
|
|
|
&video_caca,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_NETWORK_VIDEO
|
|
|
|
&video_network,
|
|
|
|
#endif
|
|
|
|
&video_null,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static video_driver_state_t video_driver_st = { 0 };
|
2023-03-25 11:57:10 +01:00
|
|
|
static const video_display_server_t *current_display_server =
|
2022-10-08 22:52:18 +02:00
|
|
|
&dispserv_null;
|
|
|
|
|
|
|
|
struct retro_hw_render_callback *video_driver_get_hw_context(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_driver_state_t *video_state_get_ptr(void)
|
|
|
|
{
|
|
|
|
return &video_driver_st;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
void *video_thread_get_ptr(video_driver_state_t *video_st)
|
|
|
|
{
|
|
|
|
const thread_video_t *thr = (const thread_video_t*)video_st->data;
|
|
|
|
if (thr)
|
|
|
|
return thr->driver_data;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_driver_get_ptr:
|
|
|
|
*
|
|
|
|
* Use this if you need the real video driver
|
|
|
|
* and driver data pointers.
|
|
|
|
*
|
|
|
|
* Returns: video driver's userdata.
|
|
|
|
**/
|
|
|
|
void *video_driver_get_ptr(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return VIDEO_DRIVER_GET_PTR_INTERNAL(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
video_driver_t *hw_render_context_driver(
|
|
|
|
enum retro_hw_context_type type, int major, int minor)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
|
|
|
#ifdef HAVE_OPENGL_CORE
|
|
|
|
return &video_gl3;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL:
|
|
|
|
#ifdef HAVE_OPENGL
|
|
|
|
return &video_gl2;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
case RETRO_HW_CONTEXT_D3D10:
|
|
|
|
#if defined(HAVE_D3D10)
|
|
|
|
return &video_d3d10;
|
|
|
|
#else
|
|
|
|
break;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
case RETRO_HW_CONTEXT_D3D11:
|
2022-10-08 22:52:18 +02:00
|
|
|
#if defined(HAVE_D3D11)
|
2023-04-11 00:54:02 +02:00
|
|
|
return &video_d3d11;
|
|
|
|
#else
|
|
|
|
break;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
case RETRO_HW_CONTEXT_D3D12:
|
2023-05-11 03:35:12 +02:00
|
|
|
#if defined(HAVE_D3D12)
|
2023-04-11 00:54:02 +02:00
|
|
|
return &video_d3d12;
|
|
|
|
#else
|
2022-10-08 22:52:18 +02:00
|
|
|
break;
|
2023-04-11 00:54:02 +02:00
|
|
|
#endif
|
|
|
|
case RETRO_HW_CONTEXT_D3D9:
|
|
|
|
#if defined(HAVE_D3D9) && defined(HAVE_HLSL)
|
|
|
|
return &video_d3d9_hlsl;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
case RETRO_HW_CONTEXT_VULKAN:
|
|
|
|
#if defined(HAVE_VULKAN)
|
|
|
|
return &video_vulkan;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
case RETRO_HW_CONTEXT_NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *hw_render_context_name(
|
|
|
|
enum retro_hw_context_type type, int major, int minor)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_OPENGL_CORE
|
|
|
|
if (type == RETRO_HW_CONTEXT_OPENGL_CORE)
|
|
|
|
return "glcore";
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENGL
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case RETRO_HW_CONTEXT_OPENGLES2:
|
|
|
|
case RETRO_HW_CONTEXT_OPENGLES3:
|
|
|
|
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL:
|
|
|
|
#ifndef HAVE_OPENGL_CORE
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
|
|
|
#endif
|
|
|
|
return "gl";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
if (type == RETRO_HW_CONTEXT_VULKAN)
|
|
|
|
return "vulkan";
|
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
#if defined(HAVE_D3D9) && defined(HAVE_HLSL)
|
|
|
|
if (type == RETRO_HW_CONTEXT_D3D9)
|
|
|
|
return "d3d9_hlsl";
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_D3D10
|
|
|
|
if (type == RETRO_HW_CONTEXT_D3D10)
|
|
|
|
return "d3d10";
|
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
#ifdef HAVE_D3D11
|
2023-04-11 00:54:02 +02:00
|
|
|
if (type == RETRO_HW_CONTEXT_D3D11)
|
2022-10-08 22:52:18 +02:00
|
|
|
return "d3d11";
|
|
|
|
#endif
|
2023-04-10 01:08:44 +01:00
|
|
|
#ifdef HAVE_D3D12
|
2023-04-11 00:54:02 +02:00
|
|
|
if (type == RETRO_HW_CONTEXT_D3D12)
|
2023-04-10 01:08:44 +01:00
|
|
|
return "d3d12";
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
return "N/A";
|
|
|
|
}
|
|
|
|
|
2023-05-29 19:39:27 +02:00
|
|
|
static enum retro_hw_context_type hw_render_context_type(const char *s)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
#ifdef HAVE_OPENGL_CORE
|
|
|
|
if (string_is_equal(s, "glcore"))
|
|
|
|
return RETRO_HW_CONTEXT_OPENGL_CORE;
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENGL
|
|
|
|
if (string_is_equal(s, "gl"))
|
|
|
|
return RETRO_HW_CONTEXT_OPENGL;
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
if (string_is_equal(s, "vulkan"))
|
|
|
|
return RETRO_HW_CONTEXT_VULKAN;
|
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
#if defined(HAVE_D3D9) && defined(HAVE_HLSL)
|
|
|
|
if (string_is_equal(s, "d3d9_hlsl"))
|
|
|
|
return RETRO_HW_CONTEXT_D3D9;
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_D3D10
|
|
|
|
if (string_is_equal(s, "d3d10"))
|
|
|
|
return RETRO_HW_CONTEXT_D3D10;
|
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
#ifdef HAVE_D3D11
|
|
|
|
if (string_is_equal(s, "d3d11"))
|
2023-04-11 00:54:02 +02:00
|
|
|
return RETRO_HW_CONTEXT_D3D11;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2023-04-11 00:54:02 +02:00
|
|
|
#ifdef HAVE_D3D12
|
|
|
|
if (string_is_equal(s, "d3d12"))
|
|
|
|
return RETRO_HW_CONTEXT_D3D12;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
return RETRO_HW_CONTEXT_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* string list stays owned by the caller and must be available at
|
|
|
|
* all times after the video driver is inited */
|
|
|
|
void video_driver_set_gpu_api_devices(
|
|
|
|
enum gfx_ctx_api api, struct string_list *list)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2023-06-14 05:43:11 +03:00
|
|
|
for (i = 0; i < (int)ARRAY_SIZE(gpu_map); i++)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
if (api == gpu_map[i].api)
|
|
|
|
{
|
|
|
|
gpu_map[i].list = list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct string_list* video_driver_get_gpu_api_devices(enum gfx_ctx_api api)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2023-06-14 05:43:11 +03:00
|
|
|
for (i = 0; i < (int)ARRAY_SIZE(gpu_map); i++)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
if (api == gpu_map[i].api)
|
|
|
|
return gpu_map[i].list;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_driver_translate_coord_viewport:
|
|
|
|
* @mouse_x : Pointer X coordinate.
|
|
|
|
* @mouse_y : Pointer Y coordinate.
|
|
|
|
* @res_x : Scaled X coordinate.
|
|
|
|
* @res_y : Scaled Y coordinate.
|
|
|
|
* @res_screen_x : Scaled screen X coordinate.
|
|
|
|
* @res_screen_y : Scaled screen Y coordinate.
|
|
|
|
*
|
|
|
|
* Translates pointer [X,Y] coordinates into scaled screen
|
|
|
|
* coordinates based on viewport info.
|
|
|
|
*
|
|
|
|
* Returns: true (1) if successful, false if video driver doesn't support
|
|
|
|
* viewport info.
|
|
|
|
**/
|
|
|
|
bool video_driver_translate_coord_viewport(
|
|
|
|
struct video_viewport *vp,
|
|
|
|
int mouse_x, int mouse_y,
|
|
|
|
int16_t *res_x, int16_t *res_y,
|
|
|
|
int16_t *res_screen_x, int16_t *res_screen_y)
|
|
|
|
{
|
|
|
|
int norm_vp_width = (int)vp->width;
|
|
|
|
int norm_vp_height = (int)vp->height;
|
|
|
|
int norm_full_vp_width = (int)vp->full_width;
|
|
|
|
int norm_full_vp_height = (int)vp->full_height;
|
|
|
|
int scaled_screen_x = -0x8000; /* OOB */
|
|
|
|
int scaled_screen_y = -0x8000; /* OOB */
|
|
|
|
int scaled_x = -0x8000; /* OOB */
|
|
|
|
int scaled_y = -0x8000; /* OOB */
|
2023-05-31 16:08:04 +02:00
|
|
|
if ( (norm_vp_width <= 0)
|
|
|
|
|| (norm_vp_height <= 0)
|
|
|
|
|| (norm_full_vp_width <= 0)
|
|
|
|
|| (norm_full_vp_height <= 0))
|
2022-10-08 22:52:18 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
|
|
|
|
scaled_screen_x = ((2 * mouse_x * 0x7fff)
|
|
|
|
/ norm_full_vp_width) - 0x7fff;
|
|
|
|
|
|
|
|
if (mouse_y >= 0 && mouse_y <= norm_full_vp_height)
|
|
|
|
scaled_screen_y = ((2 * mouse_y * 0x7fff)
|
|
|
|
/ norm_full_vp_height) - 0x7fff;
|
|
|
|
|
|
|
|
mouse_x -= vp->x;
|
|
|
|
mouse_y -= vp->y;
|
|
|
|
|
|
|
|
if (mouse_x >= 0 && mouse_x <= norm_vp_width)
|
|
|
|
scaled_x = ((2 * mouse_x * 0x7fff)
|
|
|
|
/ norm_vp_width) - 0x7fff;
|
|
|
|
else
|
|
|
|
scaled_x = -0x8000; /* OOB */
|
|
|
|
|
|
|
|
if (mouse_y >= 0 && mouse_y <= norm_vp_height)
|
|
|
|
scaled_y = ((2 * mouse_y * 0x7fff)
|
|
|
|
/ norm_vp_height) - 0x7fff;
|
|
|
|
|
|
|
|
*res_x = scaled_x;
|
|
|
|
*res_y = scaled_y;
|
|
|
|
*res_screen_x = scaled_screen_x;
|
|
|
|
*res_screen_y = scaled_screen_y;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_monitor_compute_fps_statistics(uint64_t
|
|
|
|
frame_time_count)
|
|
|
|
{
|
|
|
|
double avg_fps = 0.0;
|
|
|
|
double stddev = 0.0;
|
|
|
|
unsigned samples = 0;
|
|
|
|
|
|
|
|
if (frame_time_count <
|
|
|
|
(2 * MEASURE_FRAME_TIME_SAMPLES_COUNT))
|
|
|
|
{
|
|
|
|
RARCH_LOG(
|
|
|
|
"[Video]: Does not have enough samples for monitor refresh rate"
|
|
|
|
" estimation. Requires to run for at least %u frames.\n",
|
|
|
|
2 * MEASURE_FRAME_TIME_SAMPLES_COUNT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (video_monitor_fps_statistics(&avg_fps, &stddev, &samples))
|
|
|
|
{
|
|
|
|
RARCH_LOG("[Video]: Average monitor Hz: %.6f Hz. (%.3f %% frame time"
|
|
|
|
" deviation, based on %u last samples).\n",
|
|
|
|
avg_fps, 100.0f * stddev, samples);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_monitor_set_refresh_rate(float hz)
|
|
|
|
{
|
2023-05-19 12:05:57 +02:00
|
|
|
char msg[256];
|
|
|
|
char rate[8];
|
2022-10-08 22:52:18 +02:00
|
|
|
settings_t *settings = config_get_ptr();
|
2023-05-19 12:05:57 +02:00
|
|
|
|
|
|
|
/* Avoid message spamming if there is no change. */
|
|
|
|
if (settings->floats.video_refresh_rate == hz)
|
|
|
|
return;
|
2023-06-14 04:42:43 +02:00
|
|
|
|
2023-05-19 12:05:57 +02:00
|
|
|
snprintf(rate, sizeof(rate), "%.3f", hz);
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
|
|
msg_hash_to_str(MSG_VIDEO_REFRESH_RATE_CHANGED), rate);
|
|
|
|
|
|
|
|
/* Message is visible for twice the usual duration */
|
|
|
|
/* as modeswitch will cause monitors to go blank for a while */
|
2022-10-08 22:52:18 +02:00
|
|
|
if (settings->bools.notification_show_refresh_rate)
|
2023-05-19 12:05:57 +02:00
|
|
|
runloop_msg_queue_push(msg, 1, 360, false, NULL,
|
2022-10-08 22:52:18 +02:00
|
|
|
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
|
|
RARCH_LOG("[Video]: %s\n", msg);
|
|
|
|
|
|
|
|
configuration_set_float(settings,
|
|
|
|
settings->floats.video_refresh_rate,
|
|
|
|
hz);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_force_fallback(const char *driver)
|
|
|
|
{
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
ui_msg_window_t *msg_window = NULL;
|
|
|
|
|
|
|
|
configuration_set_string(settings,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
driver);
|
|
|
|
|
|
|
|
command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
|
|
|
|
|
|
|
|
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) && !defined(WINAPI_FAMILY)
|
|
|
|
/* UI companion driver is not inited yet, just call into it directly */
|
|
|
|
msg_window = &ui_msg_window_win32;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (msg_window)
|
|
|
|
{
|
2023-06-19 17:31:50 +02:00
|
|
|
char text[128];
|
2022-10-08 22:52:18 +02:00
|
|
|
ui_msg_window_state window_state;
|
|
|
|
char *title = strdup(msg_hash_to_str(MSG_ERROR));
|
|
|
|
|
|
|
|
text[0] = '\0';
|
|
|
|
|
|
|
|
snprintf(text, sizeof(text),
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_DRIVER_FALLBACK),
|
|
|
|
driver);
|
|
|
|
|
|
|
|
window_state.buttons = UI_MSG_WINDOW_OK;
|
|
|
|
window_state.text = strdup(text);
|
|
|
|
window_state.title = title;
|
|
|
|
window_state.window = NULL;
|
|
|
|
|
|
|
|
msg_window->error(&window_state);
|
|
|
|
|
|
|
|
free(title);
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool get_metrics_null(void *data, enum display_metric_types type,
|
|
|
|
float *value) { return false; }
|
|
|
|
|
|
|
|
static bool video_context_driver_get_metrics_null(
|
|
|
|
void *data, enum display_metric_types type,
|
|
|
|
float *value) { return false; }
|
|
|
|
|
|
|
|
void video_context_driver_destroy(gfx_ctx_driver_t *ctx_driver)
|
|
|
|
{
|
|
|
|
if (!ctx_driver)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ctx_driver->init = NULL;
|
|
|
|
ctx_driver->bind_api = NULL;
|
|
|
|
ctx_driver->swap_interval = NULL;
|
|
|
|
ctx_driver->set_video_mode = NULL;
|
|
|
|
ctx_driver->get_video_size = NULL;
|
|
|
|
ctx_driver->get_video_output_size = NULL;
|
|
|
|
ctx_driver->get_video_output_prev = NULL;
|
|
|
|
ctx_driver->get_video_output_next = NULL;
|
2023-03-25 11:57:10 +01:00
|
|
|
ctx_driver->get_metrics =
|
2022-10-08 22:52:18 +02:00
|
|
|
video_context_driver_get_metrics_null;
|
|
|
|
ctx_driver->translate_aspect = NULL;
|
|
|
|
ctx_driver->update_window_title = NULL;
|
|
|
|
ctx_driver->check_window = NULL;
|
|
|
|
ctx_driver->set_resize = NULL;
|
|
|
|
ctx_driver->suppress_screensaver = NULL;
|
|
|
|
ctx_driver->swap_buffers = NULL;
|
|
|
|
ctx_driver->input_driver = NULL;
|
|
|
|
ctx_driver->get_proc_address = NULL;
|
|
|
|
ctx_driver->image_buffer_init = NULL;
|
|
|
|
ctx_driver->image_buffer_write = NULL;
|
|
|
|
ctx_driver->show_mouse = NULL;
|
|
|
|
ctx_driver->ident = NULL;
|
|
|
|
ctx_driver->get_flags = NULL;
|
|
|
|
ctx_driver->set_flags = NULL;
|
|
|
|
ctx_driver->bind_hw_render = NULL;
|
|
|
|
ctx_driver->get_context_data = NULL;
|
|
|
|
ctx_driver->make_current = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const gfx_ctx_driver_t *video_context_driver_init(
|
|
|
|
bool core_set_shared_context,
|
|
|
|
settings_t *settings,
|
|
|
|
void *data,
|
|
|
|
const gfx_ctx_driver_t *ctx,
|
|
|
|
const char *ident,
|
|
|
|
enum gfx_ctx_api api, unsigned major,
|
|
|
|
unsigned minor, bool hw_render_ctx,
|
|
|
|
void **ctx_data)
|
|
|
|
{
|
|
|
|
if (!ctx->bind_api(data, api, major, minor))
|
|
|
|
{
|
|
|
|
RARCH_WARN("Failed to bind API (#%u, version %u.%u)"
|
|
|
|
" on context driver \"%s\".\n",
|
|
|
|
(unsigned)api, major, minor, ctx->ident);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(*ctx_data = ctx->init(data)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (ctx->bind_hw_render)
|
|
|
|
{
|
|
|
|
bool video_shared_context =
|
|
|
|
settings->bools.video_shared_context || core_set_shared_context;
|
|
|
|
|
|
|
|
ctx->bind_hw_render(*ctx_data,
|
|
|
|
video_shared_context && hw_render_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* config_get_video_driver_options:
|
|
|
|
*
|
|
|
|
* Get an enumerated list of all video driver names, separated by '|'.
|
|
|
|
*
|
|
|
|
* Returns: string listing of all video driver names, separated by '|'.
|
|
|
|
**/
|
|
|
|
const char* config_get_video_driver_options(void)
|
|
|
|
{
|
|
|
|
return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL);
|
|
|
|
}
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
void video_driver_pixel_converter_free(video_pixel_scaler_t *scalr)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
if (!scalr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (scalr->scaler)
|
|
|
|
{
|
|
|
|
scaler_ctx_gen_reset(scalr->scaler);
|
|
|
|
free(scalr->scaler);
|
|
|
|
}
|
|
|
|
if (scalr->scaler_out)
|
|
|
|
free(scalr->scaler_out);
|
|
|
|
|
|
|
|
scalr->scaler = NULL;
|
|
|
|
scalr->scaler_out = NULL;
|
|
|
|
|
|
|
|
free(scalr);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_pixel_scaler_t *video_driver_pixel_converter_init(
|
|
|
|
const enum retro_pixel_format video_driver_pix_fmt,
|
|
|
|
struct retro_hw_render_callback *hwr,
|
|
|
|
unsigned size)
|
|
|
|
{
|
|
|
|
void *scalr_out = NULL;
|
|
|
|
video_pixel_scaler_t *scalr = NULL;
|
|
|
|
struct scaler_ctx *scalr_ctx = NULL;
|
|
|
|
|
|
|
|
/* If pixel format is not 0RGB1555, we don't need to do
|
|
|
|
* any internal pixel conversion. */
|
|
|
|
if (video_driver_pix_fmt != RETRO_PIXEL_FORMAT_0RGB1555)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* No need to perform pixel conversion for HW rendering contexts. */
|
|
|
|
if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
RARCH_WARN("[Video]: 0RGB1555 pixel format is deprecated,"
|
|
|
|
" and will be slower. For 15/16-bit, RGB565"
|
|
|
|
" format is preferred.\n");
|
|
|
|
|
|
|
|
if (!(scalr = (video_pixel_scaler_t*)malloc(sizeof(*scalr))))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
scalr->scaler = NULL;
|
|
|
|
scalr->scaler_out = NULL;
|
|
|
|
|
|
|
|
if (!(scalr_ctx = (struct scaler_ctx*)calloc(1, sizeof(*scalr_ctx))))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
scalr->scaler = scalr_ctx;
|
|
|
|
scalr->scaler->scaler_type = SCALER_TYPE_POINT;
|
|
|
|
scalr->scaler->in_fmt = SCALER_FMT_0RGB1555;
|
|
|
|
/* TODO/FIXME: Pick either ARGB8888 or RGB565 depending on driver. */
|
|
|
|
scalr->scaler->out_fmt = SCALER_FMT_RGB565;
|
|
|
|
|
|
|
|
if (!scaler_ctx_gen_filter(scalr_ctx))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!(scalr_out = calloc(sizeof(uint16_t), size * size)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
scalr->scaler_out = scalr_out;
|
|
|
|
|
|
|
|
return scalr;
|
|
|
|
|
|
|
|
error:
|
|
|
|
video_driver_pixel_converter_free(scalr);
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
video_driver_filter_free();
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_gpu_record_deinit(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->record_gpu_buffer)
|
|
|
|
free(video_st->record_gpu_buffer);
|
|
|
|
video_st->record_gpu_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void recording_dump_frame(
|
|
|
|
const void *data, unsigned width,
|
|
|
|
unsigned height, size_t pitch, bool is_idle)
|
|
|
|
{
|
|
|
|
struct record_video_data ffemu_data;
|
2022-12-22 19:58:26 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
recording_state_t *record_st = recording_state_get_ptr();
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
ffemu_data.data = data;
|
|
|
|
ffemu_data.width = width;
|
|
|
|
ffemu_data.height = height;
|
|
|
|
ffemu_data.pitch = (int)pitch;
|
|
|
|
ffemu_data.is_dupe = false;
|
|
|
|
|
|
|
|
if (video_st->record_gpu_buffer)
|
|
|
|
{
|
|
|
|
struct video_viewport vp;
|
|
|
|
|
|
|
|
vp.x = 0;
|
|
|
|
vp.y = 0;
|
|
|
|
vp.width = 0;
|
|
|
|
vp.height = 0;
|
|
|
|
vp.full_width = 0;
|
|
|
|
vp.full_height = 0;
|
|
|
|
|
2023-04-23 18:23:18 +02:00
|
|
|
if (video_st->current_video && video_st->current_video->viewport_info)
|
|
|
|
video_st->current_video->viewport_info(video_st->data, &vp);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (!vp.width || !vp.height)
|
|
|
|
{
|
|
|
|
RARCH_WARN("[Recording]: %s\n",
|
|
|
|
msg_hash_to_str(MSG_VIEWPORT_SIZE_CALCULATION_FAILED));
|
|
|
|
video_driver_gpu_record_deinit();
|
|
|
|
recording_dump_frame(
|
|
|
|
data, width, height, pitch, is_idle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* User has resized. We kinda have a problem now. */
|
2023-05-30 12:19:53 +02:00
|
|
|
if ( (vp.width != record_st->gpu_width)
|
|
|
|
|| (vp.height != record_st->gpu_height))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
const char *recording_failed_str =
|
|
|
|
msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE);
|
|
|
|
RARCH_WARN("[Recording]: %s\n", recording_failed_str);
|
|
|
|
|
|
|
|
runloop_msg_queue_push(recording_failed_str,
|
|
|
|
1, 180, true,
|
|
|
|
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
|
|
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Big bottleneck.
|
|
|
|
* Since we might need to do read-backs asynchronously,
|
|
|
|
* it might take 3-4 times before this returns true. */
|
2023-05-30 12:19:53 +02:00
|
|
|
if (!( video_st->current_video->read_viewport
|
|
|
|
&& video_st->current_video->read_viewport(
|
|
|
|
video_st->data, video_st->record_gpu_buffer, is_idle)))
|
2022-10-08 22:52:18 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
ffemu_data.pitch = (int)(record_st->gpu_width * 3);
|
|
|
|
ffemu_data.width = (unsigned)record_st->gpu_width;
|
|
|
|
ffemu_data.height = (unsigned)record_st->gpu_height;
|
|
|
|
ffemu_data.data = video_st->record_gpu_buffer + (ffemu_data.height - 1) * ffemu_data.pitch;
|
|
|
|
|
|
|
|
ffemu_data.pitch = -ffemu_data.pitch;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ffemu_data.is_dupe = !data;
|
|
|
|
|
|
|
|
record_st->driver->push_video(record_st->data, &ffemu_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *video_display_server_get_ident(void)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
if (current_display_server)
|
|
|
|
return current_display_server->ident;
|
|
|
|
return FILE_PATH_UNKNOWN;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void* video_display_server_init(enum rarch_display_type type)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_display_server_destroy();
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case RARCH_DISPLAY_WIN32:
|
|
|
|
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
|
|
|
|
current_display_server = &dispserv_win32;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case RARCH_DISPLAY_X11:
|
|
|
|
#if defined(HAVE_X11)
|
|
|
|
current_display_server = &dispserv_x11;
|
2023-05-19 12:05:57 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case RARCH_DISPLAY_KMS:
|
|
|
|
#if defined(HAVE_KMS)
|
|
|
|
current_display_server = &dispserv_kms;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
#if defined(ANDROID)
|
|
|
|
current_display_server = &dispserv_android;
|
|
|
|
#else
|
|
|
|
current_display_server = &dispserv_null;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_display_server)
|
|
|
|
{
|
|
|
|
if (current_display_server->init)
|
|
|
|
video_st->current_display_server_data = current_display_server->init();
|
|
|
|
|
|
|
|
if (!string_is_empty(current_display_server->ident))
|
|
|
|
{
|
|
|
|
RARCH_LOG("[Video]: Found display server: \"%s\".\n",
|
|
|
|
current_display_server->ident);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
video_st->initial_screen_orientation =
|
|
|
|
video_display_server_get_screen_orientation();
|
|
|
|
video_st->current_screen_orientation =
|
|
|
|
video_st->initial_screen_orientation;
|
|
|
|
|
|
|
|
return video_st->current_display_server_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_display_server_destroy(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
const enum rotation initial_screen_orientation = video_st->initial_screen_orientation;
|
|
|
|
const enum rotation current_screen_orientation = video_st->current_screen_orientation;
|
|
|
|
|
|
|
|
if (initial_screen_orientation != current_screen_orientation)
|
|
|
|
video_display_server_set_screen_orientation(initial_screen_orientation);
|
|
|
|
|
|
|
|
if (current_display_server)
|
|
|
|
if (video_st->current_display_server_data)
|
|
|
|
current_display_server->destroy(video_st->current_display_server_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_set_window_opacity(unsigned opacity)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (current_display_server && current_display_server->set_window_opacity)
|
|
|
|
return current_display_server->set_window_opacity(
|
|
|
|
video_st->current_display_server_data, opacity);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_set_window_progress(int progress, bool finished)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (current_display_server && current_display_server->set_window_progress)
|
|
|
|
return current_display_server->set_window_progress(
|
|
|
|
video_st->current_display_server_data, progress, finished);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_set_window_decorations(bool on)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (current_display_server && current_display_server->set_window_decorations)
|
|
|
|
return current_display_server->set_window_decorations(
|
|
|
|
video_st->current_display_server_data, on);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_set_resolution(unsigned width, unsigned height,
|
|
|
|
int int_hz, float hz, int center, int monitor_index, int xoffset, int padjust)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2023-05-19 12:05:57 +02:00
|
|
|
RARCH_DBG("[Video]: Display server set resolution, hz: %f\n",hz);
|
2022-10-08 22:52:18 +02:00
|
|
|
if (current_display_server && current_display_server->set_resolution)
|
|
|
|
return current_display_server->set_resolution(
|
|
|
|
video_st->current_display_server_data, width, height, int_hz,
|
|
|
|
hz, center, monitor_index, xoffset, padjust);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_has_resolution_list(void)
|
|
|
|
{
|
|
|
|
return (current_display_server
|
|
|
|
&& current_display_server->get_resolution_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *video_display_server_get_resolution_list(unsigned *size)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_display_server_has_resolution_list())
|
|
|
|
return current_display_server->get_resolution_list(
|
|
|
|
video_st->current_display_server_data, size);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_has_refresh_rate(float hz)
|
|
|
|
{
|
|
|
|
unsigned i, size = 0;
|
|
|
|
bool rate_exists = false;
|
|
|
|
|
|
|
|
struct video_display_config *video_list = (struct video_display_config*)
|
|
|
|
video_display_server_get_resolution_list(&size);
|
|
|
|
|
|
|
|
if (video_list)
|
|
|
|
{
|
2023-03-13 21:10:10 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2022-10-08 22:52:18 +02:00
|
|
|
unsigned video_driver_width = video_st->width;
|
|
|
|
unsigned video_driver_height = video_st->height;
|
|
|
|
|
|
|
|
for (i = 0; i < size && !rate_exists; i++)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
if ( (video_list[i].width == video_driver_width)
|
|
|
|
&& (video_list[i].height == video_driver_height)
|
|
|
|
&& (video_list[i].refreshrate == floor(hz)))
|
2022-10-08 22:52:18 +02:00
|
|
|
rate_exists = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(video_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rate_exists;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_switch_refresh_rate_maybe(
|
|
|
|
float *refresh_rate_suggest,
|
|
|
|
bool *video_switch_refresh_rate)
|
|
|
|
{
|
|
|
|
settings_t *settings = config_get_ptr();
|
2023-06-19 07:50:26 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
float refresh_rate = *refresh_rate_suggest;
|
|
|
|
float video_refresh_rate = settings->floats.video_refresh_rate;
|
|
|
|
unsigned crt_switch_resolution = settings->uints.crt_switch_resolution;
|
|
|
|
unsigned autoswitch_refresh_rate = settings->uints.video_autoswitch_refresh_rate;
|
|
|
|
unsigned video_swap_interval = runloop_get_video_swap_interval(
|
|
|
|
settings->uints.video_swap_interval);
|
|
|
|
unsigned video_bfi = settings->uints.video_black_frame_insertion;
|
|
|
|
bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
|
|
|
|
bool exclusive_fullscreen = settings->bools.video_fullscreen && !settings->bools.video_windowed_fullscreen;
|
|
|
|
bool windowed_fullscreen = settings->bools.video_fullscreen && settings->bools.video_windowed_fullscreen;
|
|
|
|
bool all_fullscreen = settings->bools.video_fullscreen || settings->bools.video_windowed_fullscreen;
|
|
|
|
|
|
|
|
/* Roundings to PAL & NTSC standards */
|
2023-05-18 11:57:15 +03:00
|
|
|
if (refresh_rate > 49.00 && refresh_rate < 54.50)
|
2023-05-30 12:19:53 +02:00
|
|
|
refresh_rate = 50.00f;
|
2023-05-18 11:57:15 +03:00
|
|
|
else if (refresh_rate > 54.00 && refresh_rate < 60.00)
|
2023-05-30 12:19:53 +02:00
|
|
|
refresh_rate = 59.94f;
|
2023-05-18 11:57:15 +03:00
|
|
|
else if (refresh_rate > 60.00 && refresh_rate < 61.00)
|
2023-05-30 12:19:53 +02:00
|
|
|
refresh_rate = 60.00f;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Black frame insertion + swap interval multiplier */
|
2023-05-30 12:19:53 +02:00
|
|
|
refresh_rate = (refresh_rate * (video_bfi + 1.0f) * video_swap_interval);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Fallback when target refresh rate is not exposed or when below standards */
|
|
|
|
if (!video_display_server_has_refresh_rate(refresh_rate) || refresh_rate < 50)
|
|
|
|
refresh_rate = video_refresh_rate;
|
|
|
|
|
2023-05-18 11:57:15 +03:00
|
|
|
/* Replace target rate */
|
2022-10-08 22:52:18 +02:00
|
|
|
*refresh_rate_suggest = refresh_rate;
|
|
|
|
|
|
|
|
/* Try to switch display rate for the desired screen mode(s) when:
|
|
|
|
* - Not already at correct rate
|
|
|
|
* - 'CRT SwitchRes' OFF & 'Sync to Exact Content Framerate' OFF
|
|
|
|
* - Automatic refresh rate switching not OFF
|
|
|
|
*/
|
2023-05-30 12:19:53 +02:00
|
|
|
if ( (refresh_rate != video_refresh_rate)
|
2022-10-08 22:52:18 +02:00
|
|
|
&& !crt_switch_resolution
|
|
|
|
&& !vrr_runloop_enable
|
|
|
|
&& (autoswitch_refresh_rate != AUTOSWITCH_REFRESH_RATE_OFF))
|
|
|
|
{
|
|
|
|
*video_switch_refresh_rate = (
|
2023-05-30 12:19:53 +02:00
|
|
|
((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_EXCLUSIVE_FULLSCREEN) && exclusive_fullscreen)
|
|
|
|
|| ((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_WINDOWED_FULLSCREEN) && windowed_fullscreen)
|
|
|
|
|| ((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_ALL_FULLSCREEN) && all_fullscreen));
|
2023-05-18 11:57:15 +03:00
|
|
|
|
|
|
|
/* Store original refresh rate on automatic change, and
|
|
|
|
* restore it in deinit_core and main_quit, because not all
|
|
|
|
* cores announce refresh rate via SET_SYSTEM_AV_INFO */
|
|
|
|
if ( *video_switch_refresh_rate
|
|
|
|
&& !video_st->video_refresh_rate_original)
|
|
|
|
video_st->video_refresh_rate_original = video_refresh_rate;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_set_refresh_rate(float hz)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2023-05-19 12:05:57 +02:00
|
|
|
RARCH_DBG("[Video]: Display server set refresh rate %f\n", hz);
|
2022-10-08 22:52:18 +02:00
|
|
|
if (current_display_server && current_display_server->set_resolution)
|
|
|
|
return current_display_server->set_resolution(
|
|
|
|
video_st->current_display_server_data, 0, 0, (int)hz,
|
|
|
|
hz, 0, 0, 0, 0);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_display_server_restore_refresh_rate(void)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
float refresh_rate_original = video_st->video_refresh_rate_original;
|
|
|
|
float refresh_rate_current = settings->floats.video_refresh_rate;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (!refresh_rate_original || refresh_rate_current == refresh_rate_original)
|
|
|
|
return;
|
2023-05-19 12:05:57 +02:00
|
|
|
RARCH_DBG("[Video]: Display server restore refresh rate %f\n", refresh_rate_original);
|
2022-10-08 22:52:18 +02:00
|
|
|
video_monitor_set_refresh_rate(refresh_rate_original);
|
|
|
|
video_display_server_set_refresh_rate(refresh_rate_original);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *video_display_server_get_output_options(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (current_display_server && current_display_server->get_output_options)
|
|
|
|
return current_display_server->get_output_options(
|
|
|
|
video_st->current_display_server_data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_display_server_set_screen_orientation(enum rotation rotation)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2022-10-08 22:52:18 +02:00
|
|
|
if (current_display_server && current_display_server->set_screen_orientation)
|
|
|
|
{
|
|
|
|
RARCH_LOG("[Video]: Setting screen orientation to %d.\n", rotation);
|
|
|
|
video_st->current_screen_orientation = rotation;
|
|
|
|
current_display_server->set_screen_orientation(video_st->current_display_server_data, rotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_can_set_screen_orientation(void)
|
|
|
|
{
|
|
|
|
return (current_display_server && current_display_server->set_screen_orientation);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum rotation video_display_server_get_screen_orientation(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (current_display_server && current_display_server->get_screen_orientation)
|
|
|
|
return current_display_server->get_screen_orientation(
|
|
|
|
video_st->current_display_server_data);
|
|
|
|
return ORIENTATION_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_display_server_get_flags(gfx_ctx_flags_t *flags)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!flags || !current_display_server || !current_display_server->get_flags)
|
|
|
|
return false;
|
|
|
|
flags->flags = current_display_server->get_flags(
|
|
|
|
video_st->current_display_server_data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-05-31 16:08:04 +02:00
|
|
|
#ifdef HAVE_THREADS
|
2022-10-08 22:52:18 +02:00
|
|
|
bool video_driver_is_threaded(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
|
|
|
}
|
2023-05-31 16:08:04 +02:00
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
bool *video_driver_get_threaded(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
#if defined(__MACH__) && defined(__APPLE__)
|
|
|
|
/* TODO/FIXME - force threaded video to disabled on Apple for now
|
|
|
|
* until NSWindow/UIWindow concurrency issues are taken care of */
|
|
|
|
video_st->threaded = false;
|
|
|
|
#endif
|
|
|
|
return &video_st->threaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_threaded(bool val)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
#if defined(__MACH__) && defined(__APPLE__)
|
|
|
|
/* TODO/FIXME - force threaded video to disabled on Apple for now
|
|
|
|
* until NSWindow/UIWindow concurrency issues are taken care of */
|
|
|
|
video_st->threaded = false;
|
|
|
|
#else
|
|
|
|
video_st->threaded = val;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *video_driver_get_ident(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video)
|
|
|
|
return NULL;
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
if (VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st))
|
|
|
|
{
|
|
|
|
const thread_video_t *thr = (const thread_video_t*)video_st->data;
|
|
|
|
if (!thr || !thr->driver)
|
|
|
|
return NULL;
|
|
|
|
return thr->driver->ident;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return video_st->current_video->ident;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_context_driver_reset(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video_context.get_metrics)
|
|
|
|
video_st->current_video_context.get_metrics = get_metrics_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_set(const gfx_ctx_driver_t *data)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!data)
|
|
|
|
return false;
|
|
|
|
video_st->current_video_context = *data;
|
|
|
|
video_context_driver_reset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t video_driver_get_current_framebuffer(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->get_current_framebuffer)
|
|
|
|
return video_st->poke->get_current_framebuffer(video_st->data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
retro_proc_address_t video_driver_get_proc_address(const char *sym)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->get_proc_address)
|
|
|
|
return video_st->poke->get_proc_address(video_st->data, sym);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
void video_driver_filter_free(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->state_filter)
|
|
|
|
rarch_softfilter_free(video_st->state_filter);
|
2023-05-30 12:19:53 +02:00
|
|
|
video_st->state_filter = NULL;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (video_st->state_buffer)
|
|
|
|
{
|
|
|
|
#ifdef _3DS
|
|
|
|
linearFree(video_st->state_buffer);
|
|
|
|
#else
|
|
|
|
free(video_st->state_buffer);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
video_st->state_buffer = NULL;
|
|
|
|
|
|
|
|
video_st->state_scale = 0;
|
|
|
|
video_st->state_out_bpp = 0;
|
|
|
|
video_st->flags &= ~(VIDEO_FLAG_STATE_OUT_RGB32);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_init_filter(enum retro_pixel_format colfmt_int,
|
|
|
|
settings_t *settings)
|
|
|
|
{
|
|
|
|
unsigned pow2_x, pow2_y, maxsize;
|
|
|
|
void *buf = NULL;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
struct retro_game_geometry *geom = &video_st->av_info.geometry;
|
|
|
|
unsigned width = geom->max_width;
|
|
|
|
unsigned height = geom->max_height;
|
|
|
|
/* Deprecated format. Gets pre-converted. */
|
|
|
|
enum retro_pixel_format colfmt =
|
2023-05-30 12:19:53 +02:00
|
|
|
(colfmt_int == RETRO_PIXEL_FORMAT_0RGB1555)
|
|
|
|
? RETRO_PIXEL_FORMAT_RGB565 : colfmt_int;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (video_driver_is_hw_context())
|
|
|
|
{
|
|
|
|
RARCH_WARN("[Video]: Cannot use CPU filters when hardware rendering is used.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
if (!(video_st->state_filter = rarch_softfilter_new(
|
2022-10-08 22:52:18 +02:00
|
|
|
settings->paths.path_softfilter_plugin,
|
2023-05-30 12:19:53 +02:00
|
|
|
RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height)))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
RARCH_ERR("[Video]: Failed to load filter.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rarch_softfilter_get_max_output_size(
|
|
|
|
video_st->state_filter,
|
|
|
|
&width, &height);
|
|
|
|
|
|
|
|
pow2_x = next_pow2(width);
|
|
|
|
pow2_y = next_pow2(height);
|
|
|
|
maxsize = MAX(pow2_x, pow2_y);
|
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
/* On 3DS, video is disabled if the output resolution
|
|
|
|
* exceeds 2048x2048. To avoid the user being presented
|
|
|
|
* with a black screen, we therefore have to check that
|
|
|
|
* the filter upscaling buffer fits within this limit. */
|
|
|
|
if (maxsize >= 2048)
|
|
|
|
{
|
|
|
|
RARCH_ERR("[Video]: Softfilter initialization failed."
|
|
|
|
" Upscaling buffer exceeds hardware limitations.\n");
|
|
|
|
video_driver_filter_free();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
video_st->state_scale = maxsize / RARCH_SCALE_BASE;
|
|
|
|
if (rarch_softfilter_get_output_format(
|
|
|
|
video_st->state_filter) == RETRO_PIXEL_FORMAT_XRGB8888)
|
2023-05-30 12:19:53 +02:00
|
|
|
video_st->flags |= VIDEO_FLAG_STATE_OUT_RGB32;
|
2022-10-08 22:52:18 +02:00
|
|
|
else
|
2023-05-30 12:19:53 +02:00
|
|
|
video_st->flags &= ~VIDEO_FLAG_STATE_OUT_RGB32;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
video_st->state_out_bpp = (video_st->flags & VIDEO_FLAG_STATE_OUT_RGB32)
|
|
|
|
? sizeof(uint32_t) : sizeof(uint16_t);
|
|
|
|
|
|
|
|
/* TODO: Aligned output. */
|
|
|
|
#ifdef _3DS
|
|
|
|
buf = linearMemAlign(
|
|
|
|
width * height * video_st->state_out_bpp, 0x80);
|
|
|
|
#else
|
|
|
|
buf = malloc(
|
|
|
|
width * height * video_st->state_out_bpp);
|
|
|
|
#endif
|
|
|
|
if (!buf)
|
|
|
|
{
|
|
|
|
RARCH_ERR("[Video]: Softfilter initialization failed.\n");
|
|
|
|
video_driver_filter_free();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_st->state_buffer = buf;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void video_driver_free_hw_context(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_CONTEXT_LOCK(video_st);
|
|
|
|
|
|
|
|
if (video_st->hw_render.context_destroy)
|
|
|
|
video_st->hw_render.context_destroy();
|
|
|
|
|
|
|
|
memset(&video_st->hw_render, 0, sizeof(video_st->hw_render));
|
|
|
|
|
|
|
|
VIDEO_DRIVER_CONTEXT_UNLOCK(video_st);
|
|
|
|
|
|
|
|
video_st->hw_render_context_negotiation = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_free_internal(void)
|
|
|
|
{
|
|
|
|
input_driver_state_t *input_st = input_state_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
#ifdef HAVE_THREADS
|
2023-03-13 21:10:10 +02:00
|
|
|
bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
|
2023-07-08 08:43:25 -05:00
|
|
|
command_event(CMD_EVENT_OVERLAY_UNLOAD, NULL);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2022-10-28 19:36:56 +02:00
|
|
|
if (!(video_st->flags & VIDEO_FLAG_CACHE_CONTEXT))
|
2022-10-08 22:52:18 +02:00
|
|
|
video_driver_free_hw_context();
|
|
|
|
|
|
|
|
if (!(input_st->current_data == video_st->data))
|
|
|
|
{
|
|
|
|
if (input_st->current_driver)
|
|
|
|
if (input_st->current_driver->free)
|
|
|
|
input_st->current_driver->free(input_st->current_data);
|
|
|
|
if (input_st->primary_joypad)
|
|
|
|
{
|
|
|
|
const input_device_driver_t *tmp = input_st->primary_joypad;
|
|
|
|
input_st->primary_joypad = NULL;
|
|
|
|
tmp->destroy();
|
|
|
|
}
|
|
|
|
#ifdef HAVE_MFI
|
|
|
|
if (input_st->secondary_joypad)
|
|
|
|
{
|
|
|
|
const input_device_driver_t *tmp = input_st->secondary_joypad;
|
|
|
|
input_st->secondary_joypad = NULL;
|
|
|
|
tmp->destroy();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
input_st->flags &= ~INP_FLAG_KB_MAPPING_BLOCKED;
|
|
|
|
input_st->current_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( video_st->data
|
|
|
|
&& video_st->current_video
|
|
|
|
&& video_st->current_video->free)
|
|
|
|
video_st->current_video->free(video_st->data);
|
|
|
|
|
|
|
|
if (video_st->scaler_ptr)
|
|
|
|
video_driver_pixel_converter_free(video_st->scaler_ptr);
|
|
|
|
video_st->scaler_ptr = NULL;
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
video_driver_filter_free();
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
2022-12-22 21:36:32 +01:00
|
|
|
video_shader_dir_free_shader(
|
2022-10-08 22:52:18 +02:00
|
|
|
(struct rarch_dir_shader_list*)&video_st->dir_shader_list,
|
|
|
|
config_get_ptr()->bools.video_shader_remember_last_dir);
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
if (is_threaded)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (video_st->data)
|
|
|
|
video_monitor_compute_fps_statistics(video_st->frame_time_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_viewport_config(
|
|
|
|
struct retro_game_geometry *geom,
|
|
|
|
float video_aspect_ratio,
|
|
|
|
bool video_aspect_ratio_auto)
|
|
|
|
{
|
|
|
|
if (video_aspect_ratio < 0.0f)
|
|
|
|
{
|
|
|
|
if (geom->aspect_ratio > 0.0f && video_aspect_ratio_auto)
|
|
|
|
aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned base_width = geom->base_width;
|
|
|
|
unsigned base_height = geom->base_height;
|
|
|
|
|
|
|
|
/* Get around division by zero errors */
|
|
|
|
if (base_width == 0)
|
2023-05-30 12:19:53 +02:00
|
|
|
base_width = 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
if (base_height == 0)
|
2023-05-30 12:19:53 +02:00
|
|
|
base_height = 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
aspectratio_lut[ASPECT_RATIO_CONFIG].value =
|
|
|
|
(float)base_width / base_height; /* 1:1 PAR. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
aspectratio_lut[ASPECT_RATIO_CONFIG].value = video_aspect_ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_viewport_square_pixel(struct retro_game_geometry *geom)
|
|
|
|
{
|
|
|
|
unsigned len, i, aspect_x, aspect_y;
|
|
|
|
unsigned int rotation = 0;
|
|
|
|
unsigned highest = 1;
|
|
|
|
unsigned width = geom->base_width;
|
|
|
|
unsigned height = geom->base_height;
|
|
|
|
|
|
|
|
if (width == 0 || height == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rotation = retroarch_get_rotation();
|
|
|
|
len = MIN(width, height);
|
|
|
|
|
|
|
|
for (i = 1; i < len; i++)
|
|
|
|
{
|
|
|
|
if ((width % i) == 0 && (height % i) == 0)
|
|
|
|
highest = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rotation % 2)
|
|
|
|
{
|
|
|
|
aspect_x = height / highest;
|
2023-05-30 12:19:53 +02:00
|
|
|
aspect_y = width / highest;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
aspect_x = width / highest;
|
2022-10-08 22:52:18 +02:00
|
|
|
aspect_y = height / highest;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(aspectratio_lut[ASPECT_RATIO_SQUARE].name,
|
|
|
|
sizeof(aspectratio_lut[ASPECT_RATIO_SQUARE].name),
|
2022-12-19 21:36:06 +01:00
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_SQUARE_PIXEL),
|
|
|
|
aspect_x, aspect_y);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
aspectratio_lut[ASPECT_RATIO_SQUARE].value = (float)aspect_x / aspect_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_set_rotation(unsigned rotation)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video || !video_st->current_video->set_rotation)
|
|
|
|
return false;
|
|
|
|
video_st->current_video->set_rotation(video_st->data, rotation);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_set_video_mode(unsigned width,
|
|
|
|
unsigned height, bool fullscreen)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->set_video_mode)
|
|
|
|
{
|
|
|
|
video_st->poke->set_video_mode(video_st->data,
|
|
|
|
width, height, fullscreen);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_get_video_output_size(unsigned *width, unsigned *height, char *desc, size_t desc_len)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->poke || !video_st->poke->get_video_output_size)
|
|
|
|
return false;
|
|
|
|
video_st->poke->get_video_output_size(video_st->data,
|
|
|
|
width, height, desc, desc_len);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *video_driver_read_frame_raw(unsigned *width,
|
|
|
|
unsigned *height, size_t *pitch)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2023-03-25 11:57:10 +01:00
|
|
|
if ( video_st->current_video
|
2022-10-08 22:52:18 +02:00
|
|
|
&& video_st->current_video->read_frame_raw)
|
|
|
|
return video_st->current_video->read_frame_raw(
|
|
|
|
video_st->data, width,
|
|
|
|
height, pitch);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_filtering(unsigned index,
|
|
|
|
bool smooth, bool ctx_scaling)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2023-03-25 11:57:10 +01:00
|
|
|
if ( video_st->poke
|
2022-10-08 22:52:18 +02:00
|
|
|
&& video_st->poke->set_filtering)
|
|
|
|
video_st->poke->set_filtering(
|
|
|
|
video_st->data,
|
|
|
|
index, smooth, ctx_scaling);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_get_size(unsigned *width, unsigned *height)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
|
|
|
|
|
|
|
VIDEO_DRIVER_THREADED_LOCK(video_st, is_threaded);
|
|
|
|
#endif
|
|
|
|
if (width)
|
|
|
|
*width = video_st->width;
|
|
|
|
if (height)
|
|
|
|
*height = video_st->height;
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
VIDEO_DRIVER_THREADED_UNLOCK(video_st, is_threaded);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_size(unsigned width, unsigned height)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
bool is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
|
|
|
VIDEO_DRIVER_THREADED_LOCK(video_st, is_threaded);
|
2023-05-30 12:19:53 +02:00
|
|
|
video_st->width = width;
|
|
|
|
video_st->height = height;
|
2022-10-08 22:52:18 +02:00
|
|
|
VIDEO_DRIVER_THREADED_UNLOCK(video_st, is_threaded);
|
2023-05-31 16:08:04 +02:00
|
|
|
#else
|
|
|
|
video_st->width = width;
|
|
|
|
video_st->height = height;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_monitor_fps_statistics
|
|
|
|
* @refresh_rate : Monitor refresh rate.
|
|
|
|
* @deviation : Deviation from measured refresh rate.
|
|
|
|
* @sample_points : Amount of sampled points.
|
|
|
|
*
|
|
|
|
* Gets the monitor FPS statistics based on the current
|
|
|
|
* runtime.
|
|
|
|
*
|
|
|
|
* Returns: true (1) on success.
|
|
|
|
* false (0) if:
|
|
|
|
* a) threaded video mode is enabled
|
|
|
|
* b) less than 2 frame time samples.
|
2023-06-18 19:36:28 +02:00
|
|
|
* c) FPS monitor enable is off.
|
2022-10-08 22:52:18 +02:00
|
|
|
**/
|
|
|
|
bool video_monitor_fps_statistics(double *refresh_rate,
|
|
|
|
double *deviation, unsigned *sample_points)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
retro_time_t accum = 0;
|
|
|
|
retro_time_t avg = 0;
|
|
|
|
retro_time_t accum_var = 0;
|
|
|
|
unsigned samples = 0;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
if (VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st))
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((samples = MIN(MEASURE_FRAME_TIME_SAMPLES_COUNT,
|
|
|
|
(unsigned)video_st->frame_time_count)) < 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Measure statistics on frame time (microsecs), *not* FPS. */
|
|
|
|
for (i = 0; i < samples; i++)
|
|
|
|
{
|
|
|
|
accum += video_st->frame_time_samples[i];
|
|
|
|
#if 0
|
|
|
|
RARCH_LOG("[Video]: Interval #%u: %d usec / frame.\n",
|
|
|
|
i, (int)frame_time_samples[i]);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
avg = accum / samples;
|
|
|
|
|
|
|
|
/* Drop first measurement. It is likely to be bad. */
|
|
|
|
for (i = 0; i < samples; i++)
|
|
|
|
{
|
|
|
|
retro_time_t diff = video_st->frame_time_samples[i] - avg;
|
|
|
|
accum_var += diff * diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
*deviation = sqrt((double)accum_var / (samples - 1)) / avg;
|
|
|
|
|
|
|
|
if (refresh_rate)
|
|
|
|
*refresh_rate = 1000000.0 / avg;
|
|
|
|
|
|
|
|
if (sample_points)
|
|
|
|
*sample_points = samples;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
float video_driver_get_aspect_ratio(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->aspect_ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_lock_new(void)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK_FREE(video_st);
|
|
|
|
if (!video_st->display_lock)
|
|
|
|
video_st->display_lock = slock_new();
|
|
|
|
|
|
|
|
if (!video_st->context_lock)
|
|
|
|
video_st->context_lock = slock_new();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_stub_frame(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->frame_bak = video_st->current_video->frame;
|
|
|
|
video_st->current_video->frame = video_null.frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_unset_stub_frame(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->frame_bak)
|
|
|
|
video_st->current_video->frame = video_st->frame_bak;
|
|
|
|
|
|
|
|
video_st->frame_bak = NULL;
|
|
|
|
}
|
|
|
|
|
Shader Preset - Wildcard Replacement in Paths on Load (#15023)
When a simple preset loads, wildcards which are found in paths inside the presets will
be replaced with values coming from the current RetroArch context. This will operate
on both texture paths and reference paths.
This would allow you to do things like have one preset which could be used with the entire list of images from the Bezel Project
E.G.
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
would be replaced with
"/shaders/MyBackground_glcore_YabaSanshiro.png"
If no file found at that path, the path will revert to the original path, so operates as a fallback
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
* Possible wildcards/tokens to be replaced:
*
* $CONTENT-DIR$ -> Content Directory of the game rom
*
* $CORE$ -> Core name
*
* $GAME$ -> Game ROM's name
*
* $VID-DRV$ -> Video Driver: Currently active driver, possible replacement values:
* glcore
* gl
* vulkan
* d3d11
* d3d9_hlsl
* "N/A"
*
* $VID-DRV-SHADER-EXT$ -> Video Driver Shader File Extension: The extension of shaders type supported by the current video driver:
* cg
* glsl
* slang
*
* $VID-DRV-PRESET-EXT$ -> Video Driver Preset File Extension: The extension of shaders type supported by the current video driver:
* cgp
* glslp
* slangp
*
* $CORE-REQ-ROT$ -> Core Requested Rotation: Rotation the core is requesting, possible replacement values:
* CORE-REQ-ROT-0
* CORE-REQ-ROT-90
* CORE-REQ-ROT-180
* CORE-REQ-ROT-270
*
* $VID-ALLOW-CORE-ROT$ -> Video Allow Core Rotation: Reflects Retroarch's setting allowing the core requested rotation to affect the final rotation:
* VID-ALLOW-CORE-ROT-OFF
* VID-ALLOW-CORE-ROT-ON
*
* $VID-USER-ROT$ -> Video User Rotation: Rotation the core is requesting, possible replacement values, does not affect the UI:
* VID-USER-ROT-0
* VID-USER-ROT-90
* VID-USER-ROT-180
* VID-USER-ROT-270
*
* $VID-FINAL-ROT$ -> Video Final Rotation: Rotation which is the sum of the user rotation and the core rotation if it has been allowed, does not affect the UI:
* VID-FINAL-ROT-0
* VID-FINAL-ROT-90
* VID-FINAL-ROT-180
* VID-FINAL-ROT-270
*
* $SCREEN-ORIENT$ -> Screen Orientation: User adjusted screen orientation, will change windows from landscape to portrait, including the Retroarch UI:
* SCREEN-ORIENT-0
* SCREEN-ORIENT-90
* SCREEN-ORIENT-180
* SCREEN-ORIENT-270
*
* $VIEW-ASPECT-ORIENT$ -> Viewport Aspect Orientation: Orientation of the aspect ratio of the RetroArch viewport
* VIEW-ASPECT-ORIENT-HORZ
* VIEW-ASPECT-ORIENT-VERT
*
* $CORE-ASPECT-ORIENT$ -> Core Aspect Orientation: Orientation of the aspect ratio requested by the core
* CORE-ASPECT-ORIENT-HORZ
* CORE-ASPECT-ORIENT-VERT
*
* $PRESET_DIR$ -> Preset directory's name
*
* $PRESET$ -> Preset's name
*
* If no wildcards are found within the path, or the path
* after replacing the wildcards does not exist on disk,
* the path returned will be unaffected.
2023-02-26 13:06:57 -05:00
|
|
|
/* Get aspect ratio (DAR) requested by the core */
|
|
|
|
float video_driver_get_core_aspect(void)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
struct retro_game_geometry *geom = &video_st->av_info.geometry;
|
|
|
|
float out_aspect = 0;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (!geom || geom->base_width <= 0.0f || geom->base_height <= 0.0f)
|
2023-05-30 12:19:53 +02:00
|
|
|
return 0.0f;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Fallback to 1:1 pixel ratio if none provided */
|
|
|
|
if (geom->aspect_ratio > 0.0f)
|
Shader Preset - Wildcard Replacement in Paths on Load (#15023)
When a simple preset loads, wildcards which are found in paths inside the presets will
be replaced with values coming from the current RetroArch context. This will operate
on both texture paths and reference paths.
This would allow you to do things like have one preset which could be used with the entire list of images from the Bezel Project
E.G.
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
would be replaced with
"/shaders/MyBackground_glcore_YabaSanshiro.png"
If no file found at that path, the path will revert to the original path, so operates as a fallback
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
* Possible wildcards/tokens to be replaced:
*
* $CONTENT-DIR$ -> Content Directory of the game rom
*
* $CORE$ -> Core name
*
* $GAME$ -> Game ROM's name
*
* $VID-DRV$ -> Video Driver: Currently active driver, possible replacement values:
* glcore
* gl
* vulkan
* d3d11
* d3d9_hlsl
* "N/A"
*
* $VID-DRV-SHADER-EXT$ -> Video Driver Shader File Extension: The extension of shaders type supported by the current video driver:
* cg
* glsl
* slang
*
* $VID-DRV-PRESET-EXT$ -> Video Driver Preset File Extension: The extension of shaders type supported by the current video driver:
* cgp
* glslp
* slangp
*
* $CORE-REQ-ROT$ -> Core Requested Rotation: Rotation the core is requesting, possible replacement values:
* CORE-REQ-ROT-0
* CORE-REQ-ROT-90
* CORE-REQ-ROT-180
* CORE-REQ-ROT-270
*
* $VID-ALLOW-CORE-ROT$ -> Video Allow Core Rotation: Reflects Retroarch's setting allowing the core requested rotation to affect the final rotation:
* VID-ALLOW-CORE-ROT-OFF
* VID-ALLOW-CORE-ROT-ON
*
* $VID-USER-ROT$ -> Video User Rotation: Rotation the core is requesting, possible replacement values, does not affect the UI:
* VID-USER-ROT-0
* VID-USER-ROT-90
* VID-USER-ROT-180
* VID-USER-ROT-270
*
* $VID-FINAL-ROT$ -> Video Final Rotation: Rotation which is the sum of the user rotation and the core rotation if it has been allowed, does not affect the UI:
* VID-FINAL-ROT-0
* VID-FINAL-ROT-90
* VID-FINAL-ROT-180
* VID-FINAL-ROT-270
*
* $SCREEN-ORIENT$ -> Screen Orientation: User adjusted screen orientation, will change windows from landscape to portrait, including the Retroarch UI:
* SCREEN-ORIENT-0
* SCREEN-ORIENT-90
* SCREEN-ORIENT-180
* SCREEN-ORIENT-270
*
* $VIEW-ASPECT-ORIENT$ -> Viewport Aspect Orientation: Orientation of the aspect ratio of the RetroArch viewport
* VIEW-ASPECT-ORIENT-HORZ
* VIEW-ASPECT-ORIENT-VERT
*
* $CORE-ASPECT-ORIENT$ -> Core Aspect Orientation: Orientation of the aspect ratio requested by the core
* CORE-ASPECT-ORIENT-HORZ
* CORE-ASPECT-ORIENT-VERT
*
* $PRESET_DIR$ -> Preset directory's name
*
* $PRESET$ -> Preset's name
*
* If no wildcards are found within the path, or the path
* after replacing the wildcards does not exist on disk,
* the path returned will be unaffected.
2023-02-26 13:06:57 -05:00
|
|
|
out_aspect = geom->aspect_ratio;
|
2022-10-08 22:52:18 +02:00
|
|
|
else
|
Shader Preset - Wildcard Replacement in Paths on Load (#15023)
When a simple preset loads, wildcards which are found in paths inside the presets will
be replaced with values coming from the current RetroArch context. This will operate
on both texture paths and reference paths.
This would allow you to do things like have one preset which could be used with the entire list of images from the Bezel Project
E.G.
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
would be replaced with
"/shaders/MyBackground_glcore_YabaSanshiro.png"
If no file found at that path, the path will revert to the original path, so operates as a fallback
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
* Possible wildcards/tokens to be replaced:
*
* $CONTENT-DIR$ -> Content Directory of the game rom
*
* $CORE$ -> Core name
*
* $GAME$ -> Game ROM's name
*
* $VID-DRV$ -> Video Driver: Currently active driver, possible replacement values:
* glcore
* gl
* vulkan
* d3d11
* d3d9_hlsl
* "N/A"
*
* $VID-DRV-SHADER-EXT$ -> Video Driver Shader File Extension: The extension of shaders type supported by the current video driver:
* cg
* glsl
* slang
*
* $VID-DRV-PRESET-EXT$ -> Video Driver Preset File Extension: The extension of shaders type supported by the current video driver:
* cgp
* glslp
* slangp
*
* $CORE-REQ-ROT$ -> Core Requested Rotation: Rotation the core is requesting, possible replacement values:
* CORE-REQ-ROT-0
* CORE-REQ-ROT-90
* CORE-REQ-ROT-180
* CORE-REQ-ROT-270
*
* $VID-ALLOW-CORE-ROT$ -> Video Allow Core Rotation: Reflects Retroarch's setting allowing the core requested rotation to affect the final rotation:
* VID-ALLOW-CORE-ROT-OFF
* VID-ALLOW-CORE-ROT-ON
*
* $VID-USER-ROT$ -> Video User Rotation: Rotation the core is requesting, possible replacement values, does not affect the UI:
* VID-USER-ROT-0
* VID-USER-ROT-90
* VID-USER-ROT-180
* VID-USER-ROT-270
*
* $VID-FINAL-ROT$ -> Video Final Rotation: Rotation which is the sum of the user rotation and the core rotation if it has been allowed, does not affect the UI:
* VID-FINAL-ROT-0
* VID-FINAL-ROT-90
* VID-FINAL-ROT-180
* VID-FINAL-ROT-270
*
* $SCREEN-ORIENT$ -> Screen Orientation: User adjusted screen orientation, will change windows from landscape to portrait, including the Retroarch UI:
* SCREEN-ORIENT-0
* SCREEN-ORIENT-90
* SCREEN-ORIENT-180
* SCREEN-ORIENT-270
*
* $VIEW-ASPECT-ORIENT$ -> Viewport Aspect Orientation: Orientation of the aspect ratio of the RetroArch viewport
* VIEW-ASPECT-ORIENT-HORZ
* VIEW-ASPECT-ORIENT-VERT
*
* $CORE-ASPECT-ORIENT$ -> Core Aspect Orientation: Orientation of the aspect ratio requested by the core
* CORE-ASPECT-ORIENT-HORZ
* CORE-ASPECT-ORIENT-VERT
*
* $PRESET_DIR$ -> Preset directory's name
*
* $PRESET$ -> Preset's name
*
* If no wildcards are found within the path, or the path
* after replacing the wildcards does not exist on disk,
* the path returned will be unaffected.
2023-02-26 13:06:57 -05:00
|
|
|
out_aspect = (float)geom->base_width / geom->base_height;
|
|
|
|
|
2023-05-19 14:24:23 +03:00
|
|
|
/* Flip rotated aspect */
|
|
|
|
if ((retroarch_get_rotation() + retroarch_get_core_requested_rotation()) % 2)
|
2023-05-30 12:19:53 +02:00
|
|
|
return (1.0f / out_aspect);
|
2023-05-19 14:24:23 +03:00
|
|
|
|
Shader Preset - Wildcard Replacement in Paths on Load (#15023)
When a simple preset loads, wildcards which are found in paths inside the presets will
be replaced with values coming from the current RetroArch context. This will operate
on both texture paths and reference paths.
This would allow you to do things like have one preset which could be used with the entire list of images from the Bezel Project
E.G.
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
would be replaced with
"/shaders/MyBackground_glcore_YabaSanshiro.png"
If no file found at that path, the path will revert to the original path, so operates as a fallback
"/shaders/MyBackground_$VID-DRV$ _$CORE$.png"
* Possible wildcards/tokens to be replaced:
*
* $CONTENT-DIR$ -> Content Directory of the game rom
*
* $CORE$ -> Core name
*
* $GAME$ -> Game ROM's name
*
* $VID-DRV$ -> Video Driver: Currently active driver, possible replacement values:
* glcore
* gl
* vulkan
* d3d11
* d3d9_hlsl
* "N/A"
*
* $VID-DRV-SHADER-EXT$ -> Video Driver Shader File Extension: The extension of shaders type supported by the current video driver:
* cg
* glsl
* slang
*
* $VID-DRV-PRESET-EXT$ -> Video Driver Preset File Extension: The extension of shaders type supported by the current video driver:
* cgp
* glslp
* slangp
*
* $CORE-REQ-ROT$ -> Core Requested Rotation: Rotation the core is requesting, possible replacement values:
* CORE-REQ-ROT-0
* CORE-REQ-ROT-90
* CORE-REQ-ROT-180
* CORE-REQ-ROT-270
*
* $VID-ALLOW-CORE-ROT$ -> Video Allow Core Rotation: Reflects Retroarch's setting allowing the core requested rotation to affect the final rotation:
* VID-ALLOW-CORE-ROT-OFF
* VID-ALLOW-CORE-ROT-ON
*
* $VID-USER-ROT$ -> Video User Rotation: Rotation the core is requesting, possible replacement values, does not affect the UI:
* VID-USER-ROT-0
* VID-USER-ROT-90
* VID-USER-ROT-180
* VID-USER-ROT-270
*
* $VID-FINAL-ROT$ -> Video Final Rotation: Rotation which is the sum of the user rotation and the core rotation if it has been allowed, does not affect the UI:
* VID-FINAL-ROT-0
* VID-FINAL-ROT-90
* VID-FINAL-ROT-180
* VID-FINAL-ROT-270
*
* $SCREEN-ORIENT$ -> Screen Orientation: User adjusted screen orientation, will change windows from landscape to portrait, including the Retroarch UI:
* SCREEN-ORIENT-0
* SCREEN-ORIENT-90
* SCREEN-ORIENT-180
* SCREEN-ORIENT-270
*
* $VIEW-ASPECT-ORIENT$ -> Viewport Aspect Orientation: Orientation of the aspect ratio of the RetroArch viewport
* VIEW-ASPECT-ORIENT-HORZ
* VIEW-ASPECT-ORIENT-VERT
*
* $CORE-ASPECT-ORIENT$ -> Core Aspect Orientation: Orientation of the aspect ratio requested by the core
* CORE-ASPECT-ORIENT-HORZ
* CORE-ASPECT-ORIENT-VERT
*
* $PRESET_DIR$ -> Preset directory's name
*
* $PRESET$ -> Preset's name
*
* If no wildcards are found within the path, or the path
* after replacing the wildcards does not exist on disk,
* the path returned will be unaffected.
2023-02-26 13:06:57 -05:00
|
|
|
return out_aspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_viewport_core(void)
|
|
|
|
{
|
|
|
|
float core_aspect = video_driver_get_core_aspect();
|
|
|
|
if (core_aspect != 0)
|
|
|
|
aspectratio_lut[ASPECT_RATIO_CORE].value = core_aspect;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_rgba(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
video_st->flags |= VIDEO_FLAG_USE_RGBA;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_unset_rgba(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_USE_RGBA;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_supports_rgba(void)
|
|
|
|
{
|
|
|
|
bool tmp;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
tmp = video_st->flags & VIDEO_FLAG_USE_RGBA;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_hdr_support(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
video_st->flags |= VIDEO_FLAG_HDR_SUPPORT;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_unset_hdr_support(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_HDR_SUPPORT;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_supports_hdr(void)
|
|
|
|
{
|
|
|
|
bool tmp;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
VIDEO_DRIVER_LOCK(video_st);
|
|
|
|
tmp = video_st->flags & VIDEO_FLAG_HDR_SUPPORT;
|
|
|
|
VIDEO_DRIVER_UNLOCK(video_st);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_get_next_video_out(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( !video_st->poke
|
|
|
|
|| !video_st->poke->get_video_output_next
|
|
|
|
)
|
|
|
|
return false;
|
|
|
|
video_st->poke->get_video_output_next(video_st->data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_get_prev_video_out(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (
|
|
|
|
!video_st->poke
|
|
|
|
|| !video_st->poke->get_video_output_prev
|
|
|
|
)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
video_st->poke->get_video_output_prev(video_st->data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_monitor_reset(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->frame_time_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_aspect_ratio(void)
|
|
|
|
{
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
unsigned aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
|
|
|
|
switch (aspect_ratio_idx)
|
|
|
|
{
|
|
|
|
case ASPECT_RATIO_SQUARE:
|
|
|
|
video_driver_set_viewport_square_pixel(&video_st->av_info.geometry);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASPECT_RATIO_CORE:
|
|
|
|
video_driver_set_viewport_core();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASPECT_RATIO_CONFIG:
|
|
|
|
video_driver_set_viewport_config(
|
|
|
|
&video_st->av_info.geometry,
|
|
|
|
settings->floats.video_aspect_ratio,
|
|
|
|
settings->bools.video_aspect_ratio_auto);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASPECT_RATIO_FULL:
|
|
|
|
{
|
|
|
|
unsigned width = video_st->width;
|
|
|
|
unsigned height = video_st->height;
|
|
|
|
|
|
|
|
if (width != 0 && height != 0)
|
|
|
|
aspectratio_lut[ASPECT_RATIO_FULL].value = (float)width / (float)height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
video_st->aspect_ratio = aspectratio_lut[aspect_ratio_idx].value;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->set_aspect_ratio)
|
|
|
|
video_st->poke->set_aspect_ratio(video_st->data, aspect_ratio_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_update_viewport(
|
|
|
|
struct video_viewport* vp, bool force_full, bool keep_aspect)
|
|
|
|
{
|
|
|
|
float device_aspect = (float)vp->full_width / vp->full_height;
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
bool video_scale_integer = settings->bools.video_scale_integer;
|
|
|
|
unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
float video_driver_aspect_ratio = video_st->aspect_ratio;
|
|
|
|
|
|
|
|
vp->x = 0;
|
|
|
|
vp->y = 0;
|
|
|
|
vp->width = vp->full_width;
|
|
|
|
vp->height = vp->full_height;
|
|
|
|
|
|
|
|
if (video_scale_integer && !force_full)
|
|
|
|
video_viewport_get_scaled_integer(
|
|
|
|
vp,
|
|
|
|
vp->full_width,
|
|
|
|
vp->full_height,
|
|
|
|
video_driver_aspect_ratio, keep_aspect);
|
|
|
|
else if (keep_aspect && !force_full)
|
|
|
|
{
|
|
|
|
float desired_aspect = video_driver_aspect_ratio;
|
|
|
|
|
|
|
|
#if defined(HAVE_MENU)
|
|
|
|
if (video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
const struct video_viewport *custom_vp = &settings->video_viewport_custom;
|
|
|
|
vp->x = custom_vp->x;
|
|
|
|
vp->y = custom_vp->y;
|
|
|
|
vp->width = custom_vp->width;
|
|
|
|
vp->height = custom_vp->height;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
float delta;
|
|
|
|
|
|
|
|
if (fabsf(device_aspect - desired_aspect) < 0.0001f)
|
|
|
|
{
|
|
|
|
/* If the aspect ratios of screen and desired aspect
|
|
|
|
* ratio are sufficiently equal (floating point stuff),
|
|
|
|
* assume they are actually equal.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
else if (device_aspect > desired_aspect)
|
|
|
|
{
|
|
|
|
delta = (desired_aspect / device_aspect - 1.0f)
|
|
|
|
/ 2.0f + 0.5f;
|
|
|
|
vp->x = (int)roundf(vp->full_width * (0.5f - delta));
|
|
|
|
vp->width = (unsigned)roundf(2.0f * vp->full_width * delta);
|
|
|
|
vp->y = 0;
|
|
|
|
vp->height = vp->full_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vp->x = 0;
|
|
|
|
vp->width = vp->full_width;
|
|
|
|
delta = (device_aspect / desired_aspect - 1.0f)
|
|
|
|
/ 2.0f + 0.5f;
|
|
|
|
vp->y = (int)roundf(vp->full_height * (0.5f - delta));
|
|
|
|
vp->height = (unsigned)roundf(2.0f * vp->full_height * delta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-21 08:37:19 +02:00
|
|
|
|
|
|
|
#if defined(RARCH_MOBILE)
|
|
|
|
/* In portrait mode, we want viewport to gravitate to top of screen. */
|
|
|
|
if (device_aspect < 1.0f)
|
|
|
|
vp->y = 0;
|
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_restore_cached(void *settings_data)
|
|
|
|
{
|
|
|
|
settings_t *settings = (settings_t*)settings_data;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->cached_driver_id[0])
|
|
|
|
{
|
|
|
|
configuration_set_string(settings,
|
|
|
|
settings->arrays.video_driver, video_st->cached_driver_id);
|
|
|
|
|
|
|
|
video_st->cached_driver_id[0] = 0;
|
|
|
|
RARCH_LOG("[Video]: Restored video driver to \"%s\".\n",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_find_driver(
|
|
|
|
void *settings_data,
|
|
|
|
const char *prefix, bool verbosity_enabled)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
settings_t *settings = (settings_t*)settings_data;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
|
|
|
|
if (video_driver_is_hw_context())
|
|
|
|
{
|
|
|
|
struct retro_hw_render_callback *hwr =
|
|
|
|
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
|
|
|
|
int rdr_major = hwr->version_major;
|
|
|
|
int rdr_minor = hwr->version_minor;
|
|
|
|
const char *rdr_context_name = hw_render_context_name(hwr->context_type, rdr_major, rdr_minor);
|
|
|
|
enum retro_hw_context_type rdr_type = hw_render_context_type(rdr_context_name);
|
|
|
|
|
|
|
|
video_st->current_video = NULL;
|
|
|
|
|
|
|
|
if (hwr)
|
|
|
|
{
|
|
|
|
switch (rdr_type)
|
|
|
|
{
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
|
|
|
case RETRO_HW_CONTEXT_VULKAN:
|
2023-04-11 00:54:02 +02:00
|
|
|
case RETRO_HW_CONTEXT_D3D9:
|
|
|
|
case RETRO_HW_CONTEXT_D3D10:
|
|
|
|
case RETRO_HW_CONTEXT_D3D11:
|
|
|
|
case RETRO_HW_CONTEXT_D3D12:
|
|
|
|
#if defined(HAVE_VULKAN) || defined(HAVE_D3D9) || defined(HAVE_D3D10) || defined(HAVE_D3D11) || defined(HAVE_D3D12) || defined(HAVE_OPENGL_CORE)
|
2022-10-08 22:52:18 +02:00
|
|
|
RARCH_LOG("[Video]: Using HW render, %s driver forced.\n",
|
|
|
|
rdr_context_name);
|
|
|
|
|
|
|
|
if (!string_is_equal(settings->arrays.video_driver,
|
|
|
|
rdr_context_name))
|
|
|
|
{
|
|
|
|
strlcpy(video_st->cached_driver_id,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
sizeof(video_st->cached_driver_id));
|
|
|
|
configuration_set_string(settings,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
rdr_context_name);
|
|
|
|
RARCH_LOG("[Video]: \"%s\" saved as cached driver.\n",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_st->current_video = hw_render_context_driver(rdr_type, rdr_major, rdr_minor);
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case RETRO_HW_CONTEXT_OPENGL:
|
|
|
|
#if defined(HAVE_OPENGL)
|
|
|
|
RARCH_LOG("[Video]: Using HW render, OpenGL driver forced.\n");
|
|
|
|
|
|
|
|
/* If we have configured one of the HW render
|
|
|
|
* capable GL drivers, go with that. */
|
|
|
|
#if defined(HAVE_OPENGL_CORE)
|
2023-05-30 12:19:53 +02:00
|
|
|
if ( !string_is_equal(settings->arrays.video_driver, "gl")
|
|
|
|
&& !string_is_equal(settings->arrays.video_driver, "glcore"))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
strlcpy(video_st->cached_driver_id,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
sizeof(video_st->cached_driver_id));
|
|
|
|
configuration_set_string(settings,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
"glcore");
|
|
|
|
RARCH_LOG("[Video]: \"%s\" saved as cached driver.\n",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
video_st->current_video = &video_gl3;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if ( !string_is_equal(settings->arrays.video_driver, "gl"))
|
|
|
|
{
|
|
|
|
strlcpy(video_st->cached_driver_id,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
sizeof(video_st->cached_driver_id));
|
|
|
|
configuration_set_string(settings,
|
|
|
|
settings->arrays.video_driver,
|
|
|
|
"gl");
|
|
|
|
RARCH_LOG("[Video]: \"%s\" saved as cached driver.\n",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
video_st->current_video = &video_gl2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
RARCH_LOG("[Video]: Using configured \"%s\""
|
|
|
|
" driver for GL HW render.\n",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
case RETRO_HW_CONTEXT_NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frontend_driver_has_get_video_driver_func())
|
|
|
|
{
|
|
|
|
if ((video_st->current_video = (video_driver_t*)
|
|
|
|
frontend_driver_get_video_driver()))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
RARCH_WARN("[Video]: Frontend supports get_video_driver() but did not specify one.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
i = (int)driver_find_index(
|
|
|
|
"video_driver",
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
|
|
|
|
if (i >= 0)
|
|
|
|
video_st->current_video = (video_driver_t*)video_drivers[i];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (verbosity_enabled)
|
|
|
|
{
|
|
|
|
unsigned d;
|
|
|
|
RARCH_ERR("Couldn't find any %s named \"%s\"\n", prefix,
|
|
|
|
settings->arrays.video_driver);
|
|
|
|
RARCH_LOG_OUTPUT("Available %ss are:\n", prefix);
|
|
|
|
for (d = 0; video_drivers[d]; d++)
|
|
|
|
RARCH_LOG_OUTPUT("\t%s\n", video_drivers[d]->ident);
|
2022-12-22 19:58:26 +02:00
|
|
|
RARCH_WARN("Going to default to first %s..\n", prefix);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(video_st->current_video = (video_driver_t*)video_drivers[0]))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_apply_state_changes(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->apply_state_changes)
|
|
|
|
video_st->poke->apply_state_changes(video_st->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_is_hw_context(void)
|
|
|
|
{
|
|
|
|
bool is_hw_context = false;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
|
|
|
|
VIDEO_DRIVER_CONTEXT_LOCK(video_st);
|
|
|
|
is_hw_context = (video_st->hw_render.context_type
|
|
|
|
!= RETRO_HW_CONTEXT_NONE);
|
|
|
|
VIDEO_DRIVER_CONTEXT_UNLOCK(video_st);
|
|
|
|
|
|
|
|
return is_hw_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_get_viewport_info(struct video_viewport *viewport)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video || !video_st->current_video->viewport_info)
|
|
|
|
return false;
|
|
|
|
video_st->current_video->viewport_info(video_st->data, viewport);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_viewport_get_scaled_integer:
|
|
|
|
* @vp : Viewport handle
|
|
|
|
* @width : Width.
|
|
|
|
* @height : Height.
|
|
|
|
* @aspect_ratio : Aspect ratio (in float).
|
|
|
|
* @keep_aspect : Preserve aspect ratio?
|
|
|
|
*
|
|
|
|
* Gets viewport scaling dimensions based on
|
|
|
|
* scaled integer aspect ratio.
|
|
|
|
**/
|
|
|
|
void video_viewport_get_scaled_integer(struct video_viewport *vp,
|
|
|
|
unsigned width, unsigned height,
|
|
|
|
float aspect_ratio, bool keep_aspect)
|
|
|
|
{
|
|
|
|
int padding_x = 0;
|
|
|
|
int padding_y = 0;
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
bool overscale = settings->bools.video_scale_integer_overscale;
|
|
|
|
|
|
|
|
if (video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
struct video_viewport *custom_vp = &settings->video_viewport_custom;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
if (custom_vp)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
padding_x = width - custom_vp->width;
|
|
|
|
padding_y = height - custom_vp->height;
|
|
|
|
width = custom_vp->width;
|
|
|
|
height = custom_vp->height;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned base_width;
|
|
|
|
/* Use system reported sizes as these define the
|
|
|
|
* geometry for the "normal" case. */
|
|
|
|
unsigned base_height =
|
|
|
|
video_st->av_info.geometry.base_height;
|
|
|
|
unsigned int rotation = retroarch_get_rotation();
|
|
|
|
|
|
|
|
if (rotation % 2)
|
2023-05-30 22:10:03 +02:00
|
|
|
base_height = video_st->av_info.geometry.base_width;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (base_height == 0)
|
2023-05-30 22:10:03 +02:00
|
|
|
base_height = 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Account for non-square pixels.
|
|
|
|
* This is sort of contradictory with the goal of integer scale,
|
|
|
|
* but it is desirable in some cases.
|
|
|
|
*
|
|
|
|
* If square pixels are used, base_height will be equal to
|
|
|
|
* system->av_info.base_height. */
|
|
|
|
base_width = (unsigned)roundf(base_height * aspect_ratio);
|
|
|
|
|
|
|
|
/* Make sure that we don't get 0x scale ... */
|
|
|
|
if (width >= base_width && height >= base_height)
|
|
|
|
{
|
|
|
|
if (keep_aspect)
|
|
|
|
{
|
|
|
|
/* X/Y scale must be same. */
|
|
|
|
unsigned max_scale = 1;
|
|
|
|
|
|
|
|
if (overscale)
|
|
|
|
max_scale = MIN((width / base_width) + !!(width % base_width),
|
|
|
|
(height / base_height) + !!(height % base_height));
|
|
|
|
else
|
|
|
|
max_scale = MIN(width / base_width,
|
|
|
|
height / base_height);
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
padding_x = width - base_width * max_scale;
|
2022-10-08 22:52:18 +02:00
|
|
|
padding_y = height - base_height * max_scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* X/Y can be independent, each scaled as much as possible. */
|
2023-05-30 12:19:53 +02:00
|
|
|
padding_x = width % base_width;
|
2022-10-08 22:52:18 +02:00
|
|
|
padding_y = height % base_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
width -= padding_x;
|
|
|
|
height -= padding_y;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
vp->width = width;
|
|
|
|
vp->height = height;
|
|
|
|
vp->x = padding_x / 2;
|
|
|
|
vp->y = padding_y / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_display_type_set(enum rarch_display_type type)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->display_type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t video_driver_display_get(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->display;
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t video_driver_display_userdata_get(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->display_userdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_display_userdata_set(uintptr_t idx)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->display_userdata = idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_display_set(uintptr_t idx)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->display = idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum rarch_display_type video_driver_display_type_get(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->display_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_window_set(uintptr_t idx)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_st->window = idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t video_driver_window_get(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->window;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_texture_load(void *data,
|
|
|
|
enum texture_filter_type filter_type,
|
|
|
|
uintptr_t *id)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( !id
|
|
|
|
|| !video_st->poke
|
|
|
|
|| !video_st->poke->load_texture)
|
|
|
|
return false;
|
|
|
|
*id = video_st->poke->load_texture(
|
|
|
|
video_st->data, data,
|
|
|
|
VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st),
|
|
|
|
filter_type);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_texture_unload(uintptr_t *id)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( !video_st->poke
|
|
|
|
|| !video_st->poke->unload_texture)
|
|
|
|
return false;
|
|
|
|
video_st->poke->unload_texture(
|
|
|
|
video_st->data,
|
|
|
|
VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st),
|
|
|
|
*id);
|
|
|
|
*id = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_driver_cached_frame:
|
|
|
|
*
|
|
|
|
* Renders the current video frame.
|
|
|
|
**/
|
|
|
|
void video_driver_cached_frame(void)
|
|
|
|
{
|
|
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
|
|
recording_state_t *recording_st= recording_state_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
void *recording = recording_st->data;
|
|
|
|
struct retro_callbacks *cbs = &runloop_st->retro_ctx;
|
|
|
|
|
|
|
|
/* Cannot allow recording when pushing duped frames. */
|
|
|
|
recording_st->data = NULL;
|
|
|
|
|
2022-10-27 00:29:24 +02:00
|
|
|
if (runloop_st->current_core.flags & RETRO_CORE_FLAG_INITED)
|
2022-10-08 22:52:18 +02:00
|
|
|
cbs->frame_cb(
|
|
|
|
(video_st->frame_cache_data != RETRO_HW_FRAME_BUFFER_VALID)
|
2023-03-25 11:57:10 +01:00
|
|
|
? video_st->frame_cache_data
|
2022-10-08 22:52:18 +02:00
|
|
|
: NULL,
|
|
|
|
video_st->frame_cache_width,
|
|
|
|
video_st->frame_cache_height,
|
|
|
|
video_st->frame_cache_pitch);
|
|
|
|
|
|
|
|
recording_st->data = recording;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_has_focus(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return VIDEO_HAS_FOCUS(video_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t video_driver_get_window_title(char *buf, unsigned len)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (buf && (video_st->flags & VIDEO_FLAG_WINDOW_TITLE_UPDATE))
|
|
|
|
{
|
|
|
|
strlcpy(buf, video_st->window_title, len);
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_WINDOW_TITLE_UPDATE;
|
|
|
|
}
|
|
|
|
return video_st->window_title_len;
|
|
|
|
}
|
|
|
|
|
2023-07-20 13:29:46 +03:00
|
|
|
void video_driver_update_title(void *data)
|
|
|
|
{
|
|
|
|
#ifndef _XBOX
|
|
|
|
const ui_window_t *window = ui_companion_driver_get_window_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if ( video_st->flags & VIDEO_FLAG_WINDOW_TITLE_UPDATE
|
|
|
|
&& window)
|
|
|
|
{
|
|
|
|
if ( video_st->window_title[0]
|
|
|
|
&& !string_is_equal(video_st->window_title, video_st->window_title_prev))
|
|
|
|
{
|
|
|
|
window->set_title((void*)video_st->display_userdata, video_st->window_title);
|
|
|
|
strlcpy(video_st->window_title_prev, video_st->window_title, sizeof(video_st->window_title_prev));
|
|
|
|
}
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_WINDOW_TITLE_UPDATE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
void video_driver_build_info(video_frame_info_t *video_info)
|
|
|
|
{
|
|
|
|
video_viewport_t *custom_vp = NULL;
|
2022-12-22 19:58:26 +02:00
|
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
2022-10-08 22:52:18 +02:00
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
input_driver_state_t *input_st = input_state_get_ptr();
|
|
|
|
#ifdef HAVE_MENU
|
|
|
|
struct menu_state *menu_st = menu_state_get_ptr();
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
|
|
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
bool is_threaded =
|
2022-12-22 19:58:26 +02:00
|
|
|
VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
VIDEO_DRIVER_THREADED_LOCK(video_st, is_threaded);
|
|
|
|
#endif
|
|
|
|
custom_vp = &settings->video_viewport_custom;
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
|
|
video_info->widgets_active = p_dispwidget->active;
|
|
|
|
#else
|
|
|
|
video_info->widgets_active = false;
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->notifications_hidden = settings->bools.notification_show_when_menu_is_alive
|
|
|
|
&& !(menu_st->flags & MENU_ST_FLAG_ALIVE);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
video_info->refresh_rate = settings->floats.video_refresh_rate;
|
|
|
|
video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
|
|
|
|
video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
|
|
|
|
video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
|
|
|
|
video_info->crt_switch_porch_adjust = settings->ints.crt_switch_porch_adjust;
|
|
|
|
video_info->crt_switch_hires_menu = settings->bools.crt_switch_hires_menu;
|
|
|
|
video_info->black_frame_insertion = settings->uints.video_black_frame_insertion;
|
|
|
|
video_info->hard_sync = settings->bools.video_hard_sync;
|
|
|
|
video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
|
2023-01-15 09:00:15 +01:00
|
|
|
video_info->runahead = settings->bools.run_ahead_enabled;
|
|
|
|
video_info->runahead_second_instance = settings->bools.run_ahead_secondary_instance;
|
|
|
|
video_info->preemptive_frames = settings->bools.preemptive_frames_enable;
|
|
|
|
video_info->runahead_frames = settings->uints.run_ahead_frames;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->fps_show = settings->bools.video_fps_show;
|
|
|
|
video_info->memory_show = settings->bools.video_memory_show;
|
|
|
|
video_info->statistics_show = settings->bools.video_statistics_show;
|
|
|
|
video_info->framecount_show = settings->bools.video_framecount_show;
|
|
|
|
video_info->core_status_msg_show = runloop_st->core_status_msg.set;
|
|
|
|
video_info->aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
video_info->post_filter_record = settings->bools.video_post_filter_record;
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->input_menu_swap_ok_cancel_buttons
|
|
|
|
= settings->bools.input_menu_swap_ok_cancel_buttons;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->max_swapchain_images = settings->uints.video_max_swapchain_images;
|
|
|
|
video_info->windowed_fullscreen = settings->bools.video_windowed_fullscreen;
|
|
|
|
video_info->fullscreen = settings->bools.video_fullscreen
|
2022-12-22 19:58:26 +02:00
|
|
|
|| (video_st->flags & VIDEO_FLAG_FORCE_FULLSCREEN);
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->menu_mouse_enable = settings->bools.menu_mouse_enable;
|
|
|
|
video_info->monitor_index = settings->uints.video_monitor_index;
|
|
|
|
|
|
|
|
video_info->font_enable = settings->bools.video_font_enable;
|
2023-03-13 21:10:10 +02:00
|
|
|
video_info->font_size = settings->floats.video_font_size;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->font_msg_pos_x = settings->floats.video_msg_pos_x;
|
|
|
|
video_info->font_msg_pos_y = settings->floats.video_msg_pos_y;
|
|
|
|
video_info->font_msg_color_r = settings->floats.video_msg_color_r;
|
|
|
|
video_info->font_msg_color_g = settings->floats.video_msg_color_g;
|
|
|
|
video_info->font_msg_color_b = settings->floats.video_msg_color_b;
|
|
|
|
video_info->custom_vp_x = custom_vp->x;
|
|
|
|
video_info->custom_vp_y = custom_vp->y;
|
|
|
|
video_info->custom_vp_width = custom_vp->width;
|
|
|
|
video_info->custom_vp_height = custom_vp->height;
|
|
|
|
video_info->custom_vp_full_width = custom_vp->full_width;
|
|
|
|
video_info->custom_vp_full_height = custom_vp->full_height;
|
|
|
|
|
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
|
|
video_info->widgets_userdata = p_dispwidget;
|
2023-08-16 00:16:03 +02:00
|
|
|
video_info->widgets_is_paused = (video_st->flags & VIDEO_FLAG_WIDGETS_PAUSED) ? true : false;
|
|
|
|
video_info->widgets_is_fast_forwarding = (video_st->flags & VIDEO_FLAG_WIDGETS_FAST_FORWARD) ? true : false;
|
|
|
|
video_info->widgets_is_rewinding = (video_st->flags & VIDEO_FLAG_WIDGETS_REWINDING) ? true : false;
|
2022-10-08 22:52:18 +02:00
|
|
|
#else
|
|
|
|
video_info->widgets_userdata = NULL;
|
|
|
|
video_info->widgets_is_paused = false;
|
|
|
|
video_info->widgets_is_fast_forwarding = false;
|
|
|
|
video_info->widgets_is_rewinding = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
video_info->width = video_st->width;
|
|
|
|
video_info->height = video_st->height;
|
|
|
|
|
2023-08-16 00:16:03 +02:00
|
|
|
video_info->use_rgba = (video_st->flags & VIDEO_FLAG_USE_RGBA) ? true : false;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->hdr_enable = settings->bools.video_hdr_enable;
|
|
|
|
|
|
|
|
video_info->libretro_running = false;
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->msg_bgcolor_enable = settings->bools.video_msg_bgcolor_enable;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
video_info->fps_update_interval = settings->uints.fps_update_interval;
|
|
|
|
video_info->memory_update_interval = settings->uints.memory_update_interval;
|
|
|
|
|
|
|
|
#ifdef HAVE_MENU
|
2023-08-16 00:16:03 +02:00
|
|
|
video_info->menu_is_alive = (menu_st->flags & MENU_ST_FLAG_ALIVE) ? true : false;
|
|
|
|
video_info->menu_screensaver_active = (menu_st->flags & MENU_ST_FLAG_SCREENSAVER_ACTIVE) ? true : false;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->menu_footer_opacity = settings->floats.menu_footer_opacity;
|
|
|
|
video_info->menu_header_opacity = settings->floats.menu_header_opacity;
|
|
|
|
video_info->materialui_color_theme = settings->uints.menu_materialui_color_theme;
|
|
|
|
video_info->ozone_color_theme = settings->uints.menu_ozone_color_theme;
|
|
|
|
video_info->menu_shader_pipeline = settings->uints.menu_xmb_shader_pipeline;
|
|
|
|
video_info->xmb_theme = settings->uints.menu_xmb_theme;
|
|
|
|
video_info->xmb_color_theme = settings->uints.menu_xmb_color_theme;
|
|
|
|
video_info->timedate_enable = settings->bools.menu_timedate_enable;
|
|
|
|
video_info->battery_level_enable = settings->bools.menu_battery_level_enable;
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->xmb_shadows_enable = settings->bools.menu_xmb_shadows_enable;
|
|
|
|
video_info->xmb_alpha_factor = settings->uints.menu_xmb_alpha_factor;
|
|
|
|
video_info->menu_wallpaper_opacity = settings->floats.menu_wallpaper_opacity;
|
|
|
|
video_info->menu_framebuffer_opacity = settings->floats.menu_framebuffer_opacity;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->overlay_behind_menu = settings->bools.input_overlay_behind_menu;
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->libretro_running = runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED;
|
2022-10-08 22:52:18 +02:00
|
|
|
#else
|
|
|
|
video_info->menu_is_alive = false;
|
|
|
|
video_info->menu_screensaver_active = false;
|
|
|
|
video_info->menu_footer_opacity = 0.0f;
|
|
|
|
video_info->menu_header_opacity = 0.0f;
|
|
|
|
video_info->materialui_color_theme = 0;
|
|
|
|
video_info->menu_shader_pipeline = 0;
|
|
|
|
video_info->xmb_color_theme = 0;
|
|
|
|
video_info->xmb_theme = 0;
|
|
|
|
video_info->timedate_enable = false;
|
|
|
|
video_info->battery_level_enable = false;
|
|
|
|
video_info->xmb_shadows_enable = false;
|
|
|
|
video_info->xmb_alpha_factor = 0.0f;
|
|
|
|
video_info->menu_framebuffer_opacity = 0.0f;
|
|
|
|
video_info->menu_wallpaper_opacity = 0.0f;
|
|
|
|
video_info->overlay_behind_menu = false;
|
|
|
|
#endif
|
|
|
|
|
2022-10-15 12:29:07 +03:00
|
|
|
video_info->msg_queue_delay = runloop_st->msg_queue_delay;
|
2023-08-16 00:16:03 +02:00
|
|
|
video_info->runloop_is_paused = (runloop_st->flags & RUNLOOP_FLAG_PAUSED) ? true : false;
|
|
|
|
video_info->runloop_is_slowmotion = (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION) ? true : false;
|
2023-02-23 13:44:17 +01:00
|
|
|
video_info->fastforward_frameskip = settings->bools.fastforward_frameskip;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2023-06-18 22:12:03 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
#ifdef HAVE_VULKAN
|
|
|
|
/* Vulkan in Windows does mailbox emulation
|
|
|
|
* in fullscreen with vsync, effectively
|
|
|
|
* discarding frames that can't be shown,
|
|
|
|
* therefore do not do it twice. */
|
2023-06-18 21:27:34 +02:00
|
|
|
if ( video_info->fullscreen
|
2023-06-18 22:12:03 +03:00
|
|
|
&& settings->bools.video_vsync
|
2023-06-18 21:27:34 +02:00
|
|
|
&& string_is_equal(video_driver_get_ident(), "vulkan"))
|
2023-06-18 22:12:03 +03:00
|
|
|
video_info->fastforward_frameskip = false;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
video_info->input_driver_nonblock_state = input_st ? (input_st->flags & INP_FLAG_NONBLOCKING) : false;
|
2023-08-16 00:16:03 +02:00
|
|
|
video_info->input_driver_grab_mouse_state = (input_st->flags & INP_FLAG_GRAB_MOUSE_STATE) ? true : false;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info->disp_userdata = disp_get_ptr();
|
|
|
|
|
2022-12-22 19:58:26 +02:00
|
|
|
video_info->userdata = VIDEO_DRIVER_GET_PTR_INTERNAL(video_st);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_THREADS
|
|
|
|
VIDEO_DRIVER_THREADED_UNLOCK(video_st, is_threaded);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-05-29 19:13:47 +02:00
|
|
|
/**
|
|
|
|
* video_context_driver_init_first:
|
|
|
|
* @data : Input data.
|
|
|
|
* @ident : Identifier of graphics context driver to find.
|
|
|
|
* @api : API of higher-level graphics API.
|
|
|
|
* @major : Major version number of higher-level graphics API.
|
|
|
|
* @minor : Minor version number of higher-level graphics API.
|
|
|
|
* @hw_render_ctx : Request a graphics context driver capable of
|
|
|
|
* hardware rendering?
|
|
|
|
*
|
|
|
|
* Finds first suitable graphics context driver and initializes.
|
|
|
|
*
|
|
|
|
* Returns: graphics context driver if found, otherwise NULL.
|
|
|
|
**/
|
|
|
|
const gfx_ctx_driver_t *video_context_driver_init_first(void *data,
|
2022-10-08 22:52:18 +02:00
|
|
|
const char *ident, enum gfx_ctx_api api, unsigned major,
|
|
|
|
unsigned minor, bool hw_render_ctx, void **ctx_data)
|
|
|
|
{
|
|
|
|
unsigned j;
|
|
|
|
int i = -1;
|
2023-05-29 19:13:47 +02:00
|
|
|
uint32_t runloop_flags = runloop_get_flags();
|
|
|
|
settings_t *settings = config_get_ptr();
|
2022-10-08 22:52:18 +02:00
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
|
|
|
|
for (j = 0; gfx_ctx_gl_drivers[j]; j++)
|
|
|
|
{
|
|
|
|
if (string_is_equal_noncase(ident, gfx_ctx_gl_drivers[j]->ident))
|
|
|
|
{
|
|
|
|
i = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
const gfx_ctx_driver_t *ctx = video_context_driver_init(
|
2022-10-10 20:32:46 +02:00
|
|
|
runloop_flags & RUNLOOP_FLAG_CORE_SET_SHARED_CONTEXT,
|
2022-10-08 22:52:18 +02:00
|
|
|
settings,
|
|
|
|
data,
|
|
|
|
gfx_ctx_gl_drivers[i], ident,
|
|
|
|
api, major, minor, hw_render_ctx, ctx_data);
|
|
|
|
if (ctx)
|
|
|
|
{
|
|
|
|
video_st->context_data = *ctx_data;
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; gfx_ctx_gl_drivers[i]; i++)
|
|
|
|
{
|
|
|
|
const gfx_ctx_driver_t *ctx =
|
|
|
|
video_context_driver_init(
|
2022-10-10 20:32:46 +02:00
|
|
|
runloop_flags & RUNLOOP_FLAG_CORE_SET_SHARED_CONTEXT,
|
2022-10-08 22:52:18 +02:00
|
|
|
settings,
|
|
|
|
data,
|
|
|
|
gfx_ctx_gl_drivers[i], ident,
|
|
|
|
api, major, minor, hw_render_ctx, ctx_data);
|
|
|
|
|
|
|
|
if (ctx)
|
|
|
|
{
|
|
|
|
video_st->context_data = *ctx_data;
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_context_driver_free(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
video_context_driver_destroy(&video_st->current_video_context);
|
|
|
|
video_st->context_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->current_video_context.get_metrics)
|
|
|
|
return video_st->current_video_context.get_metrics(
|
|
|
|
video_st->context_data,
|
|
|
|
metrics->type,
|
|
|
|
metrics->value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_get_refresh_rate(float *refresh_rate)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video_context.get_refresh_rate || !refresh_rate)
|
|
|
|
return false;
|
|
|
|
if (!video_st->context_data)
|
|
|
|
return false;
|
|
|
|
|
2022-10-28 19:36:56 +02:00
|
|
|
if (video_st->flags & VIDEO_FLAG_CRT_SWITCHING_ACTIVE)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
float refresh_holder = 0;
|
|
|
|
if (refresh_rate)
|
|
|
|
refresh_holder =
|
|
|
|
video_st->current_video_context.get_refresh_rate(
|
|
|
|
video_st->context_data);
|
|
|
|
|
|
|
|
/* Fix for incorrect interlacing detection --
|
|
|
|
* HARD SET VSYNC TO REQUIRED REFRESH FOR CRT*/
|
|
|
|
if (refresh_holder != video_st->core_hz)
|
|
|
|
*refresh_rate = video_st->core_hz;
|
|
|
|
}
|
2022-10-28 19:36:56 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (refresh_rate)
|
|
|
|
*refresh_rate =
|
|
|
|
video_st->current_video_context.get_refresh_rate(
|
|
|
|
video_st->context_data);
|
|
|
|
}
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_get_ident(gfx_ctx_ident_t *ident)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!ident)
|
|
|
|
return false;
|
|
|
|
ident->ident = video_st->current_video_context.ident;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->current_video_context.get_flags)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (video_st->flags & VIDEO_FLAG_DEFERRED_VIDEO_CTX_DRIVER_SET_FLAGS)
|
|
|
|
{
|
2022-10-28 19:36:56 +02:00
|
|
|
flags->flags = video_st->deferred_flag_data.flags;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_st->flags &= ~VIDEO_FLAG_DEFERRED_VIDEO_CTX_DRIVER_SET_FLAGS;
|
|
|
|
}
|
2023-05-30 12:19:53 +02:00
|
|
|
else
|
|
|
|
flags->flags = video_st->current_video_context.get_flags(
|
|
|
|
video_st->context_data);
|
2022-10-08 22:52:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool video_driver_get_flags(gfx_ctx_flags_t *flags)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!video_st->poke || !video_st->poke->get_flags)
|
|
|
|
return false;
|
|
|
|
flags->flags = video_st->poke->get_flags(video_st->data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfx_ctx_flags_t video_driver_get_flags_wrapper(void)
|
|
|
|
{
|
|
|
|
gfx_ctx_flags_t flags;
|
|
|
|
flags.flags = 0;
|
|
|
|
|
|
|
|
if (!video_driver_get_flags(&flags))
|
|
|
|
video_context_driver_get_flags(&flags);
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* video_driver_test_all_flags:
|
|
|
|
* @testflag : flag to test
|
|
|
|
*
|
|
|
|
* Poll both the video and context driver's flags and test
|
|
|
|
* whether @testflag is set or not.
|
|
|
|
**/
|
|
|
|
bool video_driver_test_all_flags(enum display_flags testflag)
|
|
|
|
{
|
|
|
|
gfx_ctx_flags_t flags;
|
|
|
|
|
|
|
|
if (video_driver_get_flags(&flags))
|
|
|
|
if (BIT32_GET(flags.flags, testflag))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (video_context_driver_get_flags(&flags))
|
|
|
|
if (BIT32_GET(flags.flags, testflag))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_context_driver_set_flags(gfx_ctx_flags_t *flags)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (!flags)
|
|
|
|
return false;
|
|
|
|
|
2022-11-23 20:55:05 +01:00
|
|
|
if (!video_st->current_video_context.set_flags)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2022-11-23 20:55:05 +01:00
|
|
|
video_st->deferred_flag_data.flags = flags->flags;
|
|
|
|
video_st->flags |= VIDEO_FLAG_DEFERRED_VIDEO_CTX_DRIVER_SET_FLAGS;
|
|
|
|
return false;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
2022-11-23 20:55:05 +01:00
|
|
|
video_st->current_video_context.set_flags(
|
2022-12-22 19:58:26 +02:00
|
|
|
video_st->context_data, flags->flags);
|
2022-11-23 20:55:05 +01:00
|
|
|
return true;
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
enum gfx_ctx_api video_context_driver_get_api(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
2023-05-31 00:39:01 +02:00
|
|
|
enum gfx_ctx_api ctx_api = video_st->context_data
|
|
|
|
? video_st->current_video_context.get_api(video_st->context_data)
|
|
|
|
: GFX_CTX_NONE;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (ctx_api == GFX_CTX_NONE)
|
|
|
|
{
|
|
|
|
const char *video_ident = (video_st->current_video)
|
|
|
|
? video_st->current_video->ident
|
|
|
|
: NULL;
|
|
|
|
if (string_starts_with_size(video_ident, "d3d", STRLEN_CONST("d3d")))
|
|
|
|
{
|
|
|
|
if (string_is_equal(video_ident, "d3d9_hlsl"))
|
|
|
|
return GFX_CTX_DIRECT3D9_API;
|
|
|
|
else if (string_is_equal(video_ident, "d3d10"))
|
|
|
|
return GFX_CTX_DIRECT3D10_API;
|
|
|
|
else if (string_is_equal(video_ident, "d3d11"))
|
|
|
|
return GFX_CTX_DIRECT3D11_API;
|
|
|
|
else if (string_is_equal(video_ident, "d3d12"))
|
|
|
|
return GFX_CTX_DIRECT3D12_API;
|
|
|
|
}
|
|
|
|
if (string_starts_with_size(video_ident, "gl", STRLEN_CONST("gl")))
|
|
|
|
{
|
|
|
|
if (string_is_equal(video_ident, "gl"))
|
|
|
|
return GFX_CTX_OPENGL_API;
|
|
|
|
else if (string_is_equal(video_ident, "gl1"))
|
|
|
|
return GFX_CTX_OPENGL_API;
|
|
|
|
else if (string_is_equal(video_ident, "glcore"))
|
|
|
|
return GFX_CTX_OPENGL_API;
|
|
|
|
}
|
|
|
|
else if (string_is_equal(video_ident, "vulkan"))
|
|
|
|
return GFX_CTX_VULKAN_API;
|
|
|
|
else if (string_is_equal(video_ident, "metal"))
|
|
|
|
return GFX_CTX_METAL_API;
|
2023-02-09 23:29:45 -08:00
|
|
|
else if (string_is_equal(video_ident, "rsx"))
|
|
|
|
return GFX_CTX_RSX_API;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
return GFX_CTX_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx_api;
|
|
|
|
}
|
|
|
|
|
2023-05-31 16:08:04 +02:00
|
|
|
#if !(defined(RARCH_CONSOLE) || defined(RARCH_MOBILE))
|
2022-10-08 22:52:18 +02:00
|
|
|
bool video_driver_has_windowed(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->data && video_st->current_video->has_windowed)
|
|
|
|
return video_st->current_video->has_windowed(video_st->data);
|
|
|
|
return false;
|
|
|
|
}
|
2023-05-31 16:08:04 +02:00
|
|
|
#endif
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
void *video_driver = video_st->data;
|
|
|
|
const video_poke_interface_t *video_poke = video_st->poke;
|
|
|
|
|
|
|
|
shader->data = NULL;
|
|
|
|
if (!video_poke || !video_driver || !video_poke->get_current_shader)
|
|
|
|
return false;
|
|
|
|
shader->data = video_poke->get_current_shader(video_driver);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
float video_driver_get_refresh_rate(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
if (video_st->poke && video_st->poke->get_refresh_rate)
|
|
|
|
return video_st->poke->get_refresh_rate(video_st->data);
|
|
|
|
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_gpu_device_string(const char *str)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
strlcpy(video_st->gpu_device_string, str,
|
|
|
|
sizeof(video_st->gpu_device_string));
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* video_driver_get_gpu_device_string(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->gpu_device_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_set_gpu_api_version_string(const char *str)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
strlcpy(video_st->gpu_api_version_string, str,
|
|
|
|
sizeof(video_st->gpu_api_version_string));
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* video_driver_get_gpu_api_version_string(void)
|
|
|
|
{
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
return video_st->gpu_api_version_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
|
|
|
|
{
|
|
|
|
video_info_t video;
|
|
|
|
unsigned max_dim, scale, width, height;
|
|
|
|
video_viewport_t *custom_vp = NULL;
|
|
|
|
input_driver_t *tmp = NULL;
|
|
|
|
static uint16_t dummy_pixels[32] = {0};
|
|
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
input_driver_state_t *input_st = input_state_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
struct retro_game_geometry *geom = &video_st->av_info.geometry;
|
|
|
|
const enum retro_pixel_format
|
|
|
|
video_driver_pix_fmt = video_st->pix_fmt;
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
const char *path_softfilter_plugin = settings->paths.path_softfilter_plugin;
|
|
|
|
|
2022-11-06 11:16:20 +02:00
|
|
|
/* Init video filter only when game is running */
|
2023-05-30 12:19:53 +02:00
|
|
|
if (( runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED)
|
|
|
|
&& !string_is_empty(path_softfilter_plugin))
|
2022-10-08 22:52:18 +02:00
|
|
|
video_driver_init_filter(video_driver_pix_fmt, settings);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
max_dim = MAX(geom->max_width, geom->max_height);
|
|
|
|
scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
|
|
|
|
scale = MAX(scale, 1);
|
|
|
|
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
if (video_st->state_filter)
|
|
|
|
scale = video_st->state_scale;
|
|
|
|
#endif
|
|
|
|
|
2022-12-19 21:36:06 +01:00
|
|
|
strlcpy(aspectratio_lut[ASPECT_RATIO_CONFIG].name,
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CONFIG),
|
|
|
|
sizeof(aspectratio_lut[ASPECT_RATIO_CONFIG].name));
|
|
|
|
strlcpy(aspectratio_lut[ASPECT_RATIO_CORE].name,
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CORE_PROVIDED),
|
|
|
|
sizeof(aspectratio_lut[ASPECT_RATIO_CORE].name));
|
|
|
|
strlcpy(aspectratio_lut[ASPECT_RATIO_CUSTOM].name,
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CUSTOM),
|
|
|
|
sizeof(aspectratio_lut[ASPECT_RATIO_CUSTOM].name));
|
|
|
|
strlcpy(aspectratio_lut[ASPECT_RATIO_FULL].name,
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_FULL),
|
|
|
|
sizeof(aspectratio_lut[ASPECT_RATIO_FULL].name));
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Update core-dependent aspect ratio values. */
|
|
|
|
video_driver_set_viewport_square_pixel(geom);
|
|
|
|
video_driver_set_viewport_core();
|
|
|
|
video_driver_set_viewport_config(geom,
|
|
|
|
settings->floats.video_aspect_ratio,
|
|
|
|
settings->bools.video_aspect_ratio_auto);
|
|
|
|
|
|
|
|
/* Update CUSTOM viewport. */
|
|
|
|
custom_vp = &settings->video_viewport_custom;
|
|
|
|
|
|
|
|
if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
|
|
|
|
{
|
|
|
|
float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
|
|
|
|
aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
|
|
|
|
(custom_vp->width && custom_vp->height) ?
|
|
|
|
(float)custom_vp->width / custom_vp->height : default_aspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
/* Guard against aspect ratio index possibly being out of bounds */
|
|
|
|
unsigned new_aspect_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
if (new_aspect_idx > ASPECT_RATIO_END)
|
2023-05-30 12:19:53 +02:00
|
|
|
new_aspect_idx = settings->uints.video_aspect_ratio_idx = 0;
|
|
|
|
video_st->aspect_ratio = aspectratio_lut[new_aspect_idx].value;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
2023-03-25 11:57:10 +01:00
|
|
|
if ( settings->bools.video_fullscreen
|
2022-10-08 22:52:18 +02:00
|
|
|
|| (video_st->flags & VIDEO_FLAG_FORCE_FULLSCREEN))
|
|
|
|
{
|
|
|
|
width = settings->uints.video_fullscreen_x;
|
|
|
|
height = settings->uints.video_fullscreen_y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef __WINRT__
|
|
|
|
if (is_running_on_xbox())
|
|
|
|
{
|
|
|
|
width = uwp_get_width();
|
|
|
|
height = uwp_get_height();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2023-02-03 01:32:30 -05:00
|
|
|
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || \
|
|
|
|
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
2022-10-08 22:52:18 +02:00
|
|
|
bool window_custom_size_enable = settings->bools.video_window_save_positions;
|
|
|
|
#else
|
|
|
|
bool window_custom_size_enable = settings->bools.video_window_custom_size_enable;
|
|
|
|
#endif
|
2023-05-30 12:19:53 +02:00
|
|
|
/* TODO/FIXME: remove when the new window resizing core is hooked */
|
|
|
|
if ( window_custom_size_enable
|
|
|
|
&& settings->uints.window_position_width
|
|
|
|
&& settings->uints.window_position_height)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
width = settings->uints.window_position_width;
|
|
|
|
height = settings->uints.window_position_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
unsigned video_scale = settings->uints.video_scale;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Determine maximum allowed window dimensions
|
|
|
|
* NOTE: We cannot read the actual display
|
|
|
|
* metrics here, because the context driver
|
|
|
|
* has not yet been initialised... */
|
|
|
|
/* > Try explicitly configured values */
|
2023-05-30 12:19:53 +02:00
|
|
|
unsigned max_win_width = settings->uints.window_auto_width_max;
|
2022-10-08 22:52:18 +02:00
|
|
|
unsigned max_win_height = settings->uints.window_auto_height_max;
|
|
|
|
|
|
|
|
/* > Handle invalid settings */
|
|
|
|
if ((max_win_width == 0) || (max_win_height == 0))
|
|
|
|
{
|
|
|
|
/* > Try configured fullscreen width/height */
|
|
|
|
max_win_width = settings->uints.video_fullscreen_x;
|
|
|
|
max_win_height = settings->uints.video_fullscreen_y;
|
|
|
|
|
|
|
|
if ((max_win_width == 0) || (max_win_height == 0))
|
|
|
|
{
|
|
|
|
/* Maximum window width/size *must* be non-zero;
|
|
|
|
* if all else fails, used defined default
|
|
|
|
* maximum window size */
|
2023-05-30 12:19:53 +02:00
|
|
|
max_win_width = DEFAULT_WINDOW_AUTO_WIDTH_MAX;
|
2022-10-08 22:52:18 +02:00
|
|
|
max_win_height = DEFAULT_WINDOW_AUTO_HEIGHT_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-02 17:02:08 +02:00
|
|
|
/* rotated games send unrotated width/height and
|
|
|
|
* require special handling here because of it */
|
|
|
|
if ((retroarch_get_rotation() % 2))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2023-05-02 17:02:08 +02:00
|
|
|
/* Determine nominal window size based on
|
|
|
|
* core geometry */
|
|
|
|
if (settings->bools.video_force_aspect)
|
|
|
|
{
|
|
|
|
/* Do rounding here to simplify integer
|
|
|
|
* scale correctness. */
|
|
|
|
unsigned base_width = roundf(geom->base_width *
|
|
|
|
video_st->aspect_ratio);
|
|
|
|
width = base_width * video_scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
width = geom->base_height * video_scale;
|
|
|
|
|
|
|
|
height = geom->base_width * video_scale;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
else
|
2023-05-02 17:02:08 +02:00
|
|
|
{
|
|
|
|
/* Determine nominal window size based on
|
|
|
|
* core geometry */
|
|
|
|
if (settings->bools.video_force_aspect)
|
|
|
|
{
|
|
|
|
/* Do rounding here to simplify integer
|
|
|
|
* scale correctness. */
|
|
|
|
unsigned base_width = roundf(geom->base_height *
|
|
|
|
video_st->aspect_ratio);
|
|
|
|
width = base_width * video_scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
width = geom->base_width * video_scale;
|
|
|
|
|
|
|
|
height = geom->base_height * video_scale;
|
|
|
|
}
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Cap window size to maximum allowed values */
|
|
|
|
if ((width > max_win_width) || (height > max_win_height))
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
unsigned geom_width = (width > 0) ? width : 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
unsigned geom_height = (height > 0) ? height : 1;
|
|
|
|
float geom_aspect = (float)geom_width / (float)geom_height;
|
|
|
|
float max_win_aspect = (float)max_win_width / (float)max_win_height;
|
|
|
|
|
|
|
|
if (geom_aspect > max_win_aspect)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
width = max_win_width;
|
|
|
|
height = geom_height * max_win_width / geom_width;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Account for any possible rounding errors... */
|
|
|
|
if (height < 1)
|
|
|
|
height = 1;
|
|
|
|
else if (height > max_win_height)
|
|
|
|
height = max_win_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
height = max_win_height;
|
|
|
|
width = geom_width * max_win_height / geom_height;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Account for any possible rounding errors... */
|
|
|
|
if (width < 1)
|
2023-05-30 12:19:53 +02:00
|
|
|
width = 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
else if (width > max_win_width)
|
2023-05-30 12:19:53 +02:00
|
|
|
width = max_win_width;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width && height)
|
|
|
|
RARCH_LOG("[Video]: Set video size to: %ux%u.\n", width, height);
|
|
|
|
else
|
|
|
|
RARCH_LOG("[Video]: Set video size to: fullscreen.\n");
|
|
|
|
|
|
|
|
video_st->display_type = RARCH_DISPLAY_NONE;
|
|
|
|
video_st->display = 0;
|
|
|
|
video_st->display_userdata = 0;
|
|
|
|
video_st->window = 0;
|
|
|
|
|
|
|
|
video_st->scaler_ptr = video_driver_pixel_converter_init(
|
|
|
|
video_st->pix_fmt,
|
|
|
|
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st),
|
|
|
|
RARCH_SCALE_BASE * scale);
|
|
|
|
|
|
|
|
video.width = width;
|
|
|
|
video.height = height;
|
2022-12-22 19:58:26 +02:00
|
|
|
video.fullscreen = settings->bools.video_fullscreen
|
|
|
|
|| (video_st->flags & VIDEO_FLAG_FORCE_FULLSCREEN);
|
|
|
|
video.vsync = settings->bools.video_vsync
|
|
|
|
&& (!(runloop_st->flags & RUNLOOP_FLAG_FORCE_NONBLOCK));
|
2022-10-08 22:52:18 +02:00
|
|
|
video.force_aspect = settings->bools.video_force_aspect;
|
|
|
|
video.swap_interval = runloop_get_video_swap_interval(
|
|
|
|
settings->uints.video_swap_interval);
|
|
|
|
video.adaptive_vsync = settings->bools.video_adaptive_vsync;
|
|
|
|
#ifdef GEKKO
|
|
|
|
video.viwidth = settings->uints.video_viwidth;
|
|
|
|
video.vfilter = settings->bools.video_vfilter;
|
|
|
|
#endif
|
|
|
|
video.smooth = settings->bools.video_smooth;
|
|
|
|
video.ctx_scaling = settings->bools.video_ctx_scaling;
|
|
|
|
video.input_scale = scale;
|
2023-03-13 21:10:10 +02:00
|
|
|
video.font_enable = settings->bools.video_font_enable;
|
2022-10-08 22:52:18 +02:00
|
|
|
video.font_size = settings->floats.video_font_size;
|
|
|
|
video.path_font = settings->paths.path_font;
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
2022-12-22 19:58:26 +02:00
|
|
|
video.rgb32 = video_st->state_filter
|
|
|
|
? (video_st->flags & VIDEO_FLAG_STATE_OUT_RGB32)
|
|
|
|
: (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
|
2022-10-08 22:52:18 +02:00
|
|
|
#else
|
|
|
|
video.rgb32 =
|
2022-12-22 19:58:26 +02:00
|
|
|
(video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
video.parent = 0;
|
|
|
|
|
|
|
|
if (video.fullscreen)
|
|
|
|
video_st->flags |= VIDEO_FLAG_STARTED_FULLSCREEN;
|
|
|
|
else
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_STARTED_FULLSCREEN;
|
|
|
|
/* Reset video frame count */
|
|
|
|
video_st->frame_count = 0;
|
|
|
|
|
|
|
|
tmp = input_state_get_ptr()->current_driver;
|
|
|
|
/* Need to grab the "real" video driver interface on a reinit. */
|
2022-12-22 19:58:26 +02:00
|
|
|
video_driver_find_driver(settings, "video driver", verbosity_enabled);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_THREADS
|
2022-12-22 19:58:26 +02:00
|
|
|
video.is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
2022-10-08 22:52:18 +02:00
|
|
|
*video_is_threaded = video.is_threaded;
|
|
|
|
|
|
|
|
if (video.is_threaded)
|
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
/* Can't do hardware rendering with threaded driver currently. */
|
2022-12-22 19:58:26 +02:00
|
|
|
RARCH_LOG("[Video]: Starting threaded video driver..\n");
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
ret = video_init_thread(
|
|
|
|
(const video_driver_t**)&video_st->current_video,
|
|
|
|
&video_st->data,
|
|
|
|
&input_state_get_ptr()->current_driver,
|
|
|
|
(void**)&input_state_get_ptr()->current_data,
|
|
|
|
video_st->current_video,
|
|
|
|
video);
|
|
|
|
if (!ret)
|
|
|
|
{
|
2022-12-22 19:58:26 +02:00
|
|
|
RARCH_ERR("[Video]: Cannot open threaded video driver.. Exiting..\n");
|
2022-10-08 22:52:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
video_st->data = video_st->current_video->init(
|
|
|
|
&video,
|
|
|
|
&input_state_get_ptr()->current_driver,
|
|
|
|
(void**)&input_state_get_ptr()->current_data);
|
|
|
|
|
|
|
|
if (!video_st->data)
|
|
|
|
{
|
2022-12-22 19:58:26 +02:00
|
|
|
RARCH_ERR("[Video]: Cannot open video driver.. Exiting..\n");
|
2022-10-08 22:52:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_st->poke = NULL;
|
|
|
|
if (video_st->current_video->poke_interface)
|
|
|
|
video_st->current_video->poke_interface(
|
|
|
|
video_st->data, &video_st->poke);
|
|
|
|
|
|
|
|
if (video_st->current_video->viewport_info &&
|
|
|
|
(!custom_vp->width ||
|
|
|
|
!custom_vp->height))
|
|
|
|
{
|
|
|
|
/* Force custom viewport to have sane parameters. */
|
|
|
|
custom_vp->width = width;
|
|
|
|
custom_vp->height = height;
|
|
|
|
|
2023-04-23 18:23:18 +02:00
|
|
|
video_st->current_video->viewport_info(video_st->data, custom_vp);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
video_driver_set_rotation(retroarch_get_rotation() % 4);
|
|
|
|
|
|
|
|
video_st->current_video->suppress_screensaver(video_st->data,
|
|
|
|
settings->bools.ui_suspend_screensaver_enable);
|
|
|
|
|
|
|
|
if (!video_driver_init_input(tmp, settings, verbosity_enabled))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#ifdef HAVE_OVERLAY
|
2023-07-08 08:43:25 -05:00
|
|
|
input_overlay_unload();
|
2022-10-08 22:52:18 +02:00
|
|
|
input_overlay_init();
|
|
|
|
#endif
|
|
|
|
|
2022-10-27 00:29:24 +02:00
|
|
|
if (!(runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED))
|
2023-05-30 22:10:03 +02:00
|
|
|
{
|
|
|
|
video_st->frame_cache_data = &dummy_pixels;
|
|
|
|
video_st->frame_cache_width = 4;
|
|
|
|
video_st->frame_cache_height = 4;
|
|
|
|
video_st->frame_cache_pitch = 8;
|
|
|
|
}
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
#if defined(PSP)
|
2023-05-31 01:09:38 +02:00
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->set_texture_frame)
|
|
|
|
video_st->poke->set_texture_frame(video_st->data,
|
|
|
|
&dummy_pixels, false, 1, 1, 1.0f);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
video_context_driver_reset();
|
|
|
|
|
|
|
|
video_display_server_init(video_st->display_type);
|
|
|
|
|
|
|
|
if ((enum rotation)settings->uints.screen_orientation != ORIENTATION_NORMAL)
|
|
|
|
video_display_server_set_screen_orientation((enum rotation)settings->uints.screen_orientation);
|
|
|
|
|
|
|
|
/* Ensure that we preserve the 'grab mouse'
|
|
|
|
* state if it was enabled prior to driver
|
|
|
|
* (re-)initialisation */
|
|
|
|
if (input_st->flags & INP_FLAG_GRAB_MOUSE_STATE)
|
|
|
|
{
|
2023-05-29 18:58:23 +02:00
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->show_mouse)
|
|
|
|
video_st->poke->show_mouse(video_st->data, false);
|
2022-10-08 22:52:18 +02:00
|
|
|
if (input_driver_grab_mouse())
|
|
|
|
input_st->flags |= INP_FLAG_GRAB_MOUSE_STATE;
|
|
|
|
}
|
|
|
|
else if (video.fullscreen)
|
|
|
|
{
|
2023-05-29 18:58:23 +02:00
|
|
|
if ( video_st->poke
|
|
|
|
&& video_st->poke->show_mouse)
|
|
|
|
video_st->poke->show_mouse(video_st->data, false);
|
2022-10-08 22:52:18 +02:00
|
|
|
if (!settings->bools.video_windowed_fullscreen)
|
|
|
|
if (input_driver_grab_mouse())
|
|
|
|
input_st->flags |= INP_FLAG_GRAB_MOUSE_STATE;
|
|
|
|
}
|
|
|
|
|
2023-07-08 08:43:25 -05:00
|
|
|
#ifdef HAVE_OVERLAY
|
|
|
|
input_overlay_check_mouse_cursor();
|
|
|
|
#endif
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_frame(const void *data, unsigned width,
|
|
|
|
unsigned height, size_t pitch)
|
|
|
|
{
|
|
|
|
char status_text[128];
|
|
|
|
static char video_driver_msg[256];
|
|
|
|
static retro_time_t last_time;
|
|
|
|
static retro_time_t curr_time;
|
|
|
|
static retro_time_t fps_time;
|
|
|
|
static retro_time_t frame_time_accumulator;
|
|
|
|
static float last_fps, frame_time;
|
|
|
|
static uint64_t last_used_memory, last_total_memory;
|
|
|
|
/* Mark the start of nonblock state for
|
|
|
|
* ignoring initial previous frame time */
|
|
|
|
static int8_t nonblock_active;
|
|
|
|
/* Initialise 'last_frame_duped' to 'true'
|
|
|
|
* to ensure that the first frame is rendered */
|
2022-10-09 22:53:39 +02:00
|
|
|
static bool last_frame_duped = true;
|
|
|
|
bool render_frame = true;
|
2022-10-08 22:52:18 +02:00
|
|
|
retro_time_t new_time;
|
|
|
|
video_frame_info_t video_info;
|
2022-10-09 22:53:39 +02:00
|
|
|
size_t buf_pos = 0;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
2022-10-08 22:52:18 +02:00
|
|
|
const enum retro_pixel_format
|
2022-10-09 22:53:39 +02:00
|
|
|
video_driver_pix_fmt = video_st->pix_fmt;
|
2023-08-15 17:00:13 -05:00
|
|
|
bool runloop_idle = (runloop_st->flags & RUNLOOP_FLAG_IDLE) ? true : false;
|
2023-08-16 00:16:03 +02:00
|
|
|
bool video_driver_active = (video_st->flags & VIDEO_FLAG_ACTIVE) ? true : false;
|
2022-10-08 22:52:18 +02:00
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
2022-10-09 22:53:39 +02:00
|
|
|
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
|
|
|
|
bool widgets_active = p_dispwidget->active;
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2022-10-09 22:53:39 +02:00
|
|
|
recording_state_t *recording_st= recording_state_get_ptr();
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2022-10-09 22:53:39 +02:00
|
|
|
status_text[0] = '\0';
|
|
|
|
video_driver_msg[0] = '\0';
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (!video_driver_active)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_time = cpu_features_get_time_usec();
|
|
|
|
|
|
|
|
if (data)
|
|
|
|
video_st->frame_cache_data = data;
|
|
|
|
video_st->frame_cache_width = width;
|
|
|
|
video_st->frame_cache_height = height;
|
|
|
|
video_st->frame_cache_pitch = pitch;
|
|
|
|
|
|
|
|
if (
|
|
|
|
video_st->scaler_ptr
|
|
|
|
&& data
|
|
|
|
&& (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555)
|
|
|
|
&& (data != RETRO_HW_FRAME_BUFFER_VALID)
|
|
|
|
&& video_pixel_frame_scale(
|
|
|
|
video_st->scaler_ptr->scaler,
|
|
|
|
video_st->scaler_ptr->scaler_out,
|
|
|
|
data, width, height, pitch)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
data = video_st->scaler_ptr->scaler_out;
|
|
|
|
pitch = video_st->scaler_ptr->scaler->out_stride;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_driver_build_info(&video_info);
|
|
|
|
|
|
|
|
/* If fast forward is active and fast forward
|
2023-06-18 19:36:28 +02:00
|
|
|
* frame skipping is enabled, drop any frames
|
|
|
|
* that occur at a rate higher than the core-set
|
2022-10-08 22:52:18 +02:00
|
|
|
* refresh rate. However: We must always render
|
|
|
|
* the current frame when:
|
2023-06-18 19:36:28 +02:00
|
|
|
* - The menu is open
|
2022-10-08 22:52:18 +02:00
|
|
|
* - The last frame was NULL and the
|
|
|
|
* current frame is not (i.e. if core was
|
|
|
|
* previously sending duped frames, ensure
|
|
|
|
* that the next frame update is captured) */
|
2023-06-18 19:36:28 +02:00
|
|
|
if ( video_info.input_driver_nonblock_state
|
|
|
|
&& video_info.fastforward_frameskip
|
|
|
|
&& !(video_info.menu_is_alive
|
|
|
|
|| (last_frame_duped && !!data)))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
retro_time_t frame_time_accumulator_prev = frame_time_accumulator;
|
|
|
|
retro_time_t frame_time_delta = new_time - last_time;
|
|
|
|
|
|
|
|
/* Ignore initial previous frame time
|
|
|
|
* to prevent rubber band startup */
|
|
|
|
if (!nonblock_active)
|
|
|
|
nonblock_active = -1;
|
|
|
|
else if (nonblock_active < 0)
|
|
|
|
nonblock_active = 1;
|
|
|
|
|
|
|
|
/* Accumulate the elapsed time since the
|
|
|
|
* last frame */
|
|
|
|
if (nonblock_active > 0)
|
|
|
|
frame_time_accumulator += frame_time_delta;
|
|
|
|
|
|
|
|
/* Render frame if the accumulated time is
|
|
|
|
* greater than or equal to the expected
|
|
|
|
* core frame time */
|
2023-06-18 19:36:28 +02:00
|
|
|
render_frame = frame_time_accumulator >=
|
|
|
|
video_st->core_frame_time;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* If frame is to be rendered, subtract
|
|
|
|
* expected frame time from accumulator */
|
|
|
|
if (render_frame)
|
|
|
|
{
|
2023-06-18 19:36:28 +02:00
|
|
|
frame_time_accumulator -= video_st->core_frame_time;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Prevent external frame limiters from
|
|
|
|
* pushing fast forward ratio down to 1x */
|
2023-06-18 19:36:28 +02:00
|
|
|
if (frame_time_accumulator + frame_time_accumulator_prev < video_st->core_frame_time)
|
2022-10-08 22:52:18 +02:00
|
|
|
frame_time_accumulator -= frame_time_delta;
|
|
|
|
|
|
|
|
/* If fast forward is working correctly,
|
|
|
|
* the actual frame time will always be
|
|
|
|
* less than the expected frame time.
|
|
|
|
* But if the host cannot run the core
|
|
|
|
* fast enough to achieve at least 1x
|
|
|
|
* speed then the frame time accumulator
|
|
|
|
* will never empty and may potentially
|
|
|
|
* overflow. If a 'runaway' accumulator
|
|
|
|
* is detected, we simply reset it */
|
2023-06-18 19:36:28 +02:00
|
|
|
if (frame_time_accumulator > video_st->core_frame_time)
|
2022-10-08 22:52:18 +02:00
|
|
|
frame_time_accumulator = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nonblock_active = 0;
|
|
|
|
frame_time_accumulator = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
last_time = new_time;
|
|
|
|
last_frame_duped = !data;
|
|
|
|
|
|
|
|
/* Get the amount of frames per seconds. */
|
|
|
|
if (video_st->frame_count)
|
|
|
|
{
|
2023-05-30 12:19:53 +02:00
|
|
|
unsigned fps_update_interval = video_info.fps_update_interval;
|
|
|
|
unsigned memory_update_interval = video_info.memory_update_interval;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* set this to 1 to avoid an offset issue */
|
2023-05-30 12:19:53 +02:00
|
|
|
unsigned write_index = video_st->frame_time_count++
|
|
|
|
& (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
|
|
|
|
frame_time = new_time - fps_time;
|
|
|
|
video_st->frame_time_samples[write_index] = frame_time;
|
|
|
|
fps_time = new_time;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (video_info.fps_show)
|
|
|
|
{
|
2023-06-20 13:38:20 +02:00
|
|
|
status_text[ buf_pos] = 'F';
|
|
|
|
status_text[++buf_pos] = 'P';
|
|
|
|
status_text[++buf_pos] = 'S';
|
|
|
|
status_text[++buf_pos] = ':';
|
|
|
|
status_text[++buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '\0';
|
|
|
|
buf_pos += snprintf(
|
|
|
|
status_text + buf_pos,
|
|
|
|
sizeof(status_text) - buf_pos,
|
|
|
|
"%6.2f", last_fps);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (video_info.framecount_show)
|
|
|
|
{
|
|
|
|
if (status_text[buf_pos-1] != '\0')
|
|
|
|
{
|
2023-06-20 13:38:20 +02:00
|
|
|
status_text[ buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '|';
|
|
|
|
status_text[++buf_pos] = '|';
|
|
|
|
status_text[++buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '\0';
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
2023-06-20 13:38:20 +02:00
|
|
|
buf_pos += strlcpy(status_text + buf_pos,
|
2022-12-22 19:58:26 +02:00
|
|
|
msg_hash_to_str(MSG_FRAMES),
|
2023-06-20 13:38:20 +02:00
|
|
|
sizeof(status_text) - buf_pos);
|
2022-10-08 22:52:18 +02:00
|
|
|
status_text[buf_pos ] = ':';
|
|
|
|
status_text[++buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '\0';
|
2022-12-22 19:58:26 +02:00
|
|
|
buf_pos += snprintf(status_text + buf_pos,
|
|
|
|
sizeof(status_text) - buf_pos,
|
|
|
|
"%" PRIu64, (uint64_t)video_st->frame_count);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (video_info.memory_show)
|
|
|
|
{
|
|
|
|
if ((video_st->frame_count % memory_update_interval) == 0)
|
|
|
|
{
|
|
|
|
last_total_memory = frontend_driver_get_total_memory();
|
|
|
|
last_used_memory = last_total_memory - frontend_driver_get_free_memory();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status_text[buf_pos-1] != '\0')
|
|
|
|
{
|
|
|
|
status_text[buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '|';
|
|
|
|
status_text[++buf_pos] = '|';
|
|
|
|
status_text[++buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '\0';
|
|
|
|
}
|
|
|
|
status_text[buf_pos ] = 'M';
|
|
|
|
status_text[++buf_pos] = 'E';
|
|
|
|
status_text[++buf_pos] = 'M';
|
|
|
|
status_text[++buf_pos] = ':';
|
|
|
|
status_text[++buf_pos] = ' ';
|
|
|
|
status_text[++buf_pos] = '\0';
|
|
|
|
buf_pos += snprintf(
|
|
|
|
status_text + buf_pos, sizeof(status_text) - buf_pos, "%.2f/%.2f", last_used_memory / (1024.0f * 1024.0f),
|
|
|
|
last_total_memory / (1024.0f * 1024.0f));
|
|
|
|
status_text[buf_pos ] = 'M';
|
|
|
|
status_text[++buf_pos] = 'B';
|
|
|
|
status_text[++buf_pos] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((video_st->frame_count % fps_update_interval) == 0)
|
|
|
|
{
|
2023-06-18 12:06:12 +02:00
|
|
|
size_t new_len;
|
2022-10-08 22:52:18 +02:00
|
|
|
last_fps = TIME_TO_FPS(curr_time, new_time,
|
|
|
|
fps_update_interval);
|
|
|
|
|
2023-08-15 16:02:12 +02:00
|
|
|
new_len = strlcpy(video_st->window_title, video_st->title_buf,
|
2023-06-18 12:06:12 +02:00
|
|
|
sizeof(video_st->window_title));
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (!string_is_empty(status_text))
|
|
|
|
{
|
2023-06-20 13:38:20 +02:00
|
|
|
video_st->window_title[ new_len ] = ' ';
|
|
|
|
video_st->window_title[++new_len ] = '|';
|
|
|
|
video_st->window_title[++new_len ] = '|';
|
|
|
|
video_st->window_title[++new_len ] = ' ';
|
|
|
|
video_st->window_title[++new_len ] = '\0';
|
|
|
|
new_len += strlcpy(
|
|
|
|
video_st->window_title + new_len,
|
|
|
|
status_text,
|
|
|
|
sizeof(video_st->window_title) - new_len);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
2023-06-18 12:06:12 +02:00
|
|
|
curr_time = new_time;
|
|
|
|
video_st->window_title_len = new_len;
|
|
|
|
video_st->flags |= VIDEO_FLAG_WINDOW_TITLE_UPDATE;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
curr_time = fps_time = new_time;
|
|
|
|
|
|
|
|
video_st->window_title_len = strlcpy(
|
|
|
|
video_st->window_title,
|
|
|
|
video_st->title_buf,
|
|
|
|
sizeof(video_st->window_title));
|
|
|
|
|
|
|
|
if (video_info.fps_show)
|
|
|
|
buf_pos = strlcpy(status_text,
|
|
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE),
|
|
|
|
sizeof(status_text));
|
|
|
|
|
|
|
|
video_st->flags |= VIDEO_FLAG_WINDOW_TITLE_UPDATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add core status message to status text */
|
|
|
|
if (video_info.core_status_msg_show)
|
|
|
|
{
|
|
|
|
/* Note: We need to lock a mutex here. Strictly
|
|
|
|
* speaking, runloop_core_status_msg is not part
|
|
|
|
* of the message queue, but:
|
|
|
|
* - It may be implemented as a queue in the future
|
|
|
|
* - It seems unnecessary to create a new slock_t
|
|
|
|
* object for this type of message when
|
|
|
|
* _runloop_msg_queue_lock is already available
|
|
|
|
* We therefore just call runloop_msg_queue_lock()/
|
|
|
|
* runloop_msg_queue_unlock() in this case */
|
|
|
|
RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
|
|
|
|
|
|
|
|
/* Check whether duration timer has elapsed */
|
|
|
|
runloop_st->core_status_msg.duration -= anim_get_ptr()->delta_time;
|
|
|
|
|
|
|
|
if (runloop_st->core_status_msg.duration < 0.0f)
|
|
|
|
{
|
|
|
|
runloop_st->core_status_msg.str[0] = '\0';
|
|
|
|
runloop_st->core_status_msg.priority = 0;
|
|
|
|
runloop_st->core_status_msg.duration = 0.0f;
|
|
|
|
runloop_st->core_status_msg.set = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If status text is already set, add status
|
|
|
|
* message at the end */
|
|
|
|
if (!string_is_empty(status_text))
|
|
|
|
{
|
2023-06-18 12:06:12 +02:00
|
|
|
status_text[buf_pos ] = ' ';
|
2023-06-20 13:38:20 +02:00
|
|
|
status_text[++buf_pos ] = '|';
|
|
|
|
status_text[++buf_pos ] = '|';
|
|
|
|
status_text[++buf_pos ] = ' ';
|
|
|
|
status_text[++buf_pos ] = '\0';
|
2023-07-14 18:05:54 +02:00
|
|
|
strlcpy(
|
2023-06-20 13:38:20 +02:00
|
|
|
status_text + buf_pos,
|
2022-10-08 22:52:18 +02:00
|
|
|
runloop_st->core_status_msg.str,
|
2023-06-20 13:38:20 +02:00
|
|
|
sizeof(status_text) - buf_pos);
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
else
|
2023-07-14 18:05:54 +02:00
|
|
|
strlcpy(status_text,
|
2022-10-08 22:52:18 +02:00
|
|
|
runloop_st->core_status_msg.str,
|
|
|
|
sizeof(status_text));
|
|
|
|
}
|
|
|
|
|
|
|
|
RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Slightly messy code,
|
|
|
|
* but we really need to do processing before blocking on VSync
|
|
|
|
* for best possible scheduling.
|
|
|
|
*/
|
|
|
|
if (
|
|
|
|
(
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
!video_st->state_filter ||
|
|
|
|
#endif
|
|
|
|
!video_info.post_filter_record
|
|
|
|
|| !data
|
|
|
|
|| video_st->record_gpu_buffer
|
|
|
|
) && recording_st->data
|
|
|
|
&& recording_st->driver
|
|
|
|
&& recording_st->driver->push_video)
|
|
|
|
recording_dump_frame(
|
|
|
|
data, width, height,
|
|
|
|
pitch, runloop_idle);
|
|
|
|
|
|
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
|
|
if (render_frame && data && video_st->state_filter)
|
|
|
|
{
|
|
|
|
unsigned output_width = 0;
|
|
|
|
unsigned output_height = 0;
|
|
|
|
unsigned output_pitch = 0;
|
|
|
|
|
|
|
|
rarch_softfilter_get_output_size(video_st->state_filter,
|
|
|
|
&output_width, &output_height, width, height);
|
|
|
|
|
|
|
|
output_pitch = (output_width) * video_st->state_out_bpp;
|
|
|
|
|
|
|
|
rarch_softfilter_process(video_st->state_filter,
|
|
|
|
video_st->state_buffer, output_pitch,
|
|
|
|
data, width, height, pitch);
|
|
|
|
|
2023-05-30 12:19:53 +02:00
|
|
|
if ( video_info.post_filter_record
|
2022-10-08 22:52:18 +02:00
|
|
|
&& recording_st->data
|
|
|
|
&& recording_st->driver
|
|
|
|
&& recording_st->driver->push_video)
|
|
|
|
recording_dump_frame(
|
|
|
|
video_st->state_buffer,
|
|
|
|
output_width, output_height, output_pitch,
|
|
|
|
runloop_idle);
|
|
|
|
|
|
|
|
data = video_st->state_buffer;
|
|
|
|
width = output_width;
|
|
|
|
height = output_height;
|
|
|
|
pitch = output_pitch;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-10-15 12:29:07 +03:00
|
|
|
if (runloop_st->msg_queue_delay > 0)
|
|
|
|
runloop_st->msg_queue_delay--;
|
|
|
|
else if (runloop_st->msg_queue_size > 0)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
/* If widgets are currently enabled, then
|
|
|
|
* messages were pushed to the queue before
|
|
|
|
* widgets were initialised - in this case, the
|
|
|
|
* first item in the message queue should be
|
|
|
|
* extracted and pushed to the widget message
|
|
|
|
* queue instead */
|
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
|
|
if (widgets_active)
|
|
|
|
{
|
|
|
|
msg_queue_entry_t msg_entry;
|
2022-10-15 12:29:07 +03:00
|
|
|
bool msg_found = false;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
|
|
|
|
msg_found = msg_queue_extract(
|
|
|
|
&runloop_st->msg_queue, &msg_entry);
|
2022-10-15 12:29:07 +03:00
|
|
|
runloop_st->msg_queue_size = msg_queue_size(
|
2022-10-08 22:52:18 +02:00
|
|
|
&runloop_st->msg_queue);
|
|
|
|
RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
|
|
|
|
|
|
|
|
if (msg_found)
|
|
|
|
gfx_widgets_msg_queue_push(
|
|
|
|
NULL,
|
|
|
|
msg_entry.msg,
|
|
|
|
roundf((float)msg_entry.duration / 60.0f * 1000.0f),
|
|
|
|
msg_entry.title,
|
|
|
|
msg_entry.icon,
|
|
|
|
msg_entry.category,
|
|
|
|
msg_entry.prio,
|
|
|
|
false,
|
|
|
|
video_info.menu_is_alive
|
|
|
|
);
|
|
|
|
}
|
|
|
|
/* ...otherwise, just output message via
|
|
|
|
* regular OSD notification text (if enabled) */
|
|
|
|
else if (video_info.font_enable)
|
|
|
|
#else
|
|
|
|
if (video_info.font_enable)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
const char *msg = NULL;
|
|
|
|
RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
|
|
|
|
msg = msg_queue_pull(&runloop_st->msg_queue);
|
2022-10-15 12:29:07 +03:00
|
|
|
runloop_st->msg_queue_size = msg_queue_size(&runloop_st->msg_queue);
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
if (msg)
|
|
|
|
strlcpy(video_driver_msg, msg, sizeof(video_driver_msg));
|
|
|
|
RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (render_frame && video_info.statistics_show)
|
|
|
|
{
|
|
|
|
audio_statistics_t audio_stats;
|
2023-03-13 21:10:10 +02:00
|
|
|
char latency_stats[128];
|
|
|
|
char tmp[128];
|
|
|
|
size_t len;
|
2022-10-08 22:52:18 +02:00
|
|
|
double stddev = 0.0;
|
2023-03-13 21:10:10 +02:00
|
|
|
float font_size_scale = video_info.font_size / 100;
|
2022-10-08 22:52:18 +02:00
|
|
|
struct retro_system_av_info *av_info = &video_st->av_info;
|
2023-03-13 21:10:10 +02:00
|
|
|
unsigned red = 235;
|
|
|
|
unsigned green = 235;
|
|
|
|
unsigned blue = 235;
|
2022-10-08 22:52:18 +02:00
|
|
|
unsigned alpha = 255;
|
2023-05-30 12:19:53 +02:00
|
|
|
float scale = ((float)video_info.height / 480)
|
2023-03-13 21:10:10 +02:00
|
|
|
* 0.50f * (DEFAULT_FONT_SIZE / video_info.font_size);
|
2023-05-30 12:19:53 +02:00
|
|
|
/* Clamp scale */
|
|
|
|
if (scale < font_size_scale)
|
|
|
|
scale = font_size_scale;
|
|
|
|
if (scale > 1.00f)
|
|
|
|
scale = 1.00f;
|
2023-03-13 21:10:10 +02:00
|
|
|
|
|
|
|
if (scale > font_size_scale)
|
|
|
|
{
|
|
|
|
scale *= 100;
|
|
|
|
scale = ceil(scale);
|
|
|
|
scale /= 100;
|
|
|
|
}
|
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
audio_stats.samples = 0;
|
|
|
|
audio_stats.average_buffer_saturation = 0.0f;
|
|
|
|
audio_stats.std_deviation_percentage = 0.0f;
|
|
|
|
audio_stats.close_to_underrun = 0.0f;
|
|
|
|
audio_stats.close_to_blocking = 0.0f;
|
|
|
|
|
|
|
|
video_monitor_fps_statistics(NULL, &stddev, NULL);
|
|
|
|
|
2023-03-13 21:10:10 +02:00
|
|
|
video_info.osd_stat_params.x = 0.008f;
|
|
|
|
video_info.osd_stat_params.y = 0.960f;
|
2023-03-18 15:56:32 +02:00
|
|
|
video_info.osd_stat_params.text_align = TEXT_ALIGN_LEFT;
|
2023-03-13 21:10:10 +02:00
|
|
|
video_info.osd_stat_params.scale = scale;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info.osd_stat_params.full_screen = true;
|
2023-03-13 21:10:10 +02:00
|
|
|
video_info.osd_stat_params.drop_x = (video_info.font_size / DEFAULT_FONT_SIZE) * 3;
|
|
|
|
video_info.osd_stat_params.drop_y = (video_info.font_size / DEFAULT_FONT_SIZE) * -3;
|
|
|
|
video_info.osd_stat_params.drop_mod = 0.1f;
|
|
|
|
video_info.osd_stat_params.drop_alpha = 0.9f;
|
2023-06-18 19:36:28 +02:00
|
|
|
video_info.osd_stat_params.color = COLOR_ABGR(
|
|
|
|
alpha, blue, green, red);
|
|
|
|
|
|
|
|
audio_compute_buffer_statistics(&audio_stats);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2023-03-13 21:10:10 +02:00
|
|
|
latency_stats[0] = '\0';
|
|
|
|
tmp[0] = '\0';
|
|
|
|
len = 0;
|
|
|
|
|
2023-04-30 17:26:27 +02:00
|
|
|
/* TODO/FIXME - localize */
|
2023-03-13 21:10:10 +02:00
|
|
|
if (video_st->frame_delay_target > 0)
|
|
|
|
len = snprintf(tmp, sizeof(latency_stats),
|
|
|
|
" Frame Delay: %2u ms\n"
|
|
|
|
" - Target: %2u ms\n",
|
|
|
|
video_st->frame_delay_effective,
|
|
|
|
video_st->frame_delay_target);
|
2023-01-15 09:00:15 +01:00
|
|
|
|
|
|
|
if (video_info.runahead && !video_info.runahead_second_instance)
|
2023-03-13 21:10:10 +02:00
|
|
|
len = snprintf(tmp + len, sizeof(latency_stats),
|
|
|
|
" Run-Ahead: %2u frames\n"
|
|
|
|
" - Single Instance\n",
|
|
|
|
video_info.runahead_frames);
|
2023-01-15 09:00:15 +01:00
|
|
|
else if (video_info.runahead && video_info.runahead_second_instance)
|
2023-03-13 21:10:10 +02:00
|
|
|
len = snprintf(tmp + len, sizeof(latency_stats),
|
|
|
|
" Run-Ahead: %2u frames\n"
|
|
|
|
" - Second Instance\n",
|
|
|
|
video_info.runahead_frames);
|
2023-01-15 09:00:15 +01:00
|
|
|
else if (video_info.preemptive_frames)
|
2023-03-13 21:10:10 +02:00
|
|
|
len = snprintf(tmp + len, sizeof(latency_stats),
|
|
|
|
" Run-Ahead: %2u frames\n"
|
|
|
|
" - Preemptive Frames\n",
|
|
|
|
video_info.runahead_frames);
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
{
|
2023-04-30 17:26:27 +02:00
|
|
|
/* TODO/FIXME - localize */
|
2023-06-18 12:06:12 +02:00
|
|
|
size_t _len = strlcpy(latency_stats, "LATENCY\n", sizeof(latency_stats));
|
|
|
|
strlcpy(latency_stats + _len, tmp, sizeof(latency_stats) - _len);
|
2023-03-13 21:10:10 +02:00
|
|
|
}
|
2023-01-15 09:00:15 +01:00
|
|
|
|
2023-04-30 17:26:27 +02:00
|
|
|
/* TODO/FIXME - localize */
|
2022-10-08 22:52:18 +02:00
|
|
|
snprintf(video_info.stat_text,
|
|
|
|
sizeof(video_info.stat_text),
|
2023-03-13 21:10:10 +02:00
|
|
|
"CORE AV_INFO\n"
|
|
|
|
" Size: %u x %u\n"
|
|
|
|
" Max Size: %u x %u\n"
|
|
|
|
" Aspect: %3.3f\n"
|
|
|
|
" FPS: %3.2f\n"
|
|
|
|
" Sample Rate: %6.2f\n"
|
2023-04-14 21:15:54 +03:00
|
|
|
"VIDEO: %s\n"
|
2023-03-13 21:10:10 +02:00
|
|
|
" Viewport: %d x %d\n"
|
|
|
|
" Refresh: %5.2f hz\n"
|
|
|
|
" Frame Rate: %5.2f fps\n"
|
|
|
|
" Frame Time: %5.2f ms\n"
|
|
|
|
" - Deviation: %5.2f %%\n"
|
|
|
|
" Frames: %5" PRIu64"\n"
|
2023-04-14 21:15:54 +03:00
|
|
|
"AUDIO: %s\n"
|
2023-03-13 21:10:10 +02:00
|
|
|
" Saturation: %5.2f %%\n"
|
|
|
|
" Deviation: %5.2f %%\n"
|
|
|
|
" Underrun: %5.2f %%\n"
|
|
|
|
" Blocking: %5.2f %%\n"
|
|
|
|
" Samples: %5d\n"
|
|
|
|
"%s",
|
|
|
|
av_info->geometry.base_width,
|
|
|
|
av_info->geometry.base_height,
|
|
|
|
av_info->geometry.max_width,
|
|
|
|
av_info->geometry.max_height,
|
|
|
|
av_info->geometry.aspect_ratio,
|
|
|
|
av_info->timing.fps,
|
|
|
|
av_info->timing.sample_rate,
|
2023-04-14 21:15:54 +03:00
|
|
|
video_st->current_video->ident,
|
2023-03-13 21:10:10 +02:00
|
|
|
video_info.width,
|
|
|
|
video_info.height,
|
|
|
|
video_info.refresh_rate,
|
2022-10-08 22:52:18 +02:00
|
|
|
last_fps,
|
|
|
|
frame_time / 1000.0f,
|
|
|
|
100.0f * stddev,
|
2023-01-15 09:00:15 +01:00
|
|
|
video_st->frame_count,
|
2023-04-14 21:15:54 +03:00
|
|
|
audio_state_get_ptr()->current_audio->ident,
|
2022-10-08 22:52:18 +02:00
|
|
|
audio_stats.average_buffer_saturation,
|
|
|
|
audio_stats.std_deviation_percentage,
|
|
|
|
audio_stats.close_to_underrun,
|
|
|
|
audio_stats.close_to_blocking,
|
|
|
|
audio_stats.samples,
|
2023-03-13 21:10:10 +02:00
|
|
|
latency_stats);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* TODO/FIXME - add OSD chat text here */
|
|
|
|
}
|
|
|
|
|
2023-03-25 11:57:10 +01:00
|
|
|
if (render_frame
|
|
|
|
&& video_st->current_video
|
2022-10-08 22:52:18 +02:00
|
|
|
&& video_st->current_video->frame)
|
|
|
|
{
|
|
|
|
if (video_st->current_video->frame(
|
|
|
|
video_st->data, data, width, height,
|
|
|
|
video_st->frame_count, (unsigned)pitch,
|
|
|
|
video_info.menu_screensaver_active || video_info.notifications_hidden ? "" : video_driver_msg,
|
|
|
|
&video_info))
|
|
|
|
video_st->flags |= VIDEO_FLAG_ACTIVE;
|
|
|
|
else
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_ACTIVE;
|
|
|
|
}
|
|
|
|
|
2023-05-31 16:47:43 +02:00
|
|
|
video_st->frame_count++;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Display the status text, with a higher priority. */
|
|
|
|
if ( ( video_info.fps_show
|
|
|
|
|| video_info.framecount_show
|
|
|
|
|| video_info.memory_show
|
|
|
|
|| video_info.core_status_msg_show
|
|
|
|
)
|
|
|
|
&& !video_info.menu_screensaver_active
|
|
|
|
&& !video_info.notifications_hidden
|
|
|
|
)
|
|
|
|
{
|
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
|
|
if (widgets_active)
|
|
|
|
strlcpy(
|
2022-10-09 22:53:39 +02:00
|
|
|
p_dispwidget->gfx_widgets_status_text,
|
2022-10-08 22:52:18 +02:00
|
|
|
status_text,
|
2022-10-09 22:53:39 +02:00
|
|
|
sizeof(p_dispwidget->gfx_widgets_status_text)
|
2022-10-08 22:52:18 +02:00
|
|
|
);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
runloop_msg_queue_push(status_text, 2, 1, true, NULL,
|
|
|
|
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(HAVE_CRTSWITCHRES)
|
|
|
|
/* trigger set resolution*/
|
|
|
|
if (video_info.crt_switch_resolution)
|
|
|
|
{
|
|
|
|
unsigned native_width = width;
|
|
|
|
bool dynamic_super_width = false;
|
|
|
|
|
|
|
|
video_st->flags |= VIDEO_FLAG_CRT_SWITCHING_ACTIVE;
|
|
|
|
|
|
|
|
switch (video_info.crt_switch_resolution_super)
|
|
|
|
{
|
|
|
|
case 2560:
|
|
|
|
case 3840:
|
|
|
|
case 1920:
|
|
|
|
width = video_info.crt_switch_resolution_super;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
dynamic_super_width = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
crt_switch_res_core(
|
|
|
|
&video_st->crt_switch_st,
|
|
|
|
native_width, width,
|
|
|
|
height,
|
|
|
|
video_st->core_hz,
|
2023-06-14 04:42:43 +02:00
|
|
|
retroarch_get_rotation() & 1,
|
2022-10-08 22:52:18 +02:00
|
|
|
video_info.crt_switch_resolution,
|
|
|
|
video_info.crt_switch_center_adjust,
|
|
|
|
video_info.crt_switch_porch_adjust,
|
|
|
|
video_info.monitor_index,
|
|
|
|
dynamic_super_width,
|
|
|
|
video_info.crt_switch_resolution_super,
|
|
|
|
video_info.crt_switch_hires_menu);
|
|
|
|
}
|
|
|
|
else if (!video_info.crt_switch_resolution)
|
|
|
|
#endif
|
|
|
|
video_st->flags &= ~VIDEO_FLAG_CRT_SWITCHING_ACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void video_driver_reinit_context(settings_t *settings, int flags)
|
|
|
|
{
|
|
|
|
/* RARCH_DRIVER_CTL_UNINIT clears the callback struct so we
|
|
|
|
* need to make sure to keep a copy */
|
|
|
|
struct retro_hw_render_callback hwr_copy;
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
struct retro_hw_render_callback *hwr =
|
|
|
|
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
|
|
|
|
const struct retro_hw_render_context_negotiation_interface *iface =
|
|
|
|
video_st->hw_render_context_negotiation;
|
|
|
|
memcpy(&hwr_copy, hwr, sizeof(hwr_copy));
|
|
|
|
|
Add microphone support via a new driver (#14731)
* Some slight fixes
* Update libretro.h
* Log calls to RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE
* Finish proof-of-concept for mic support
- It works, but doesn't support floating-point audio yet
- It may need to be resampled, too
* Add macros that aren't available in SDL 2
* Comment out a variable definition for now
- For C89 compliance
* Add some comments for clarity
* Let ALSA tolerate a null new_rate
* Partial ALSA microphone support
- Not yet tested
- Mic is created and destroyed
- Mic can also be paused or unpaused
- Mic is paused or unpaused with the rest of the driver
- Microphone is not yet read
* Install error logging in the ALSA driver
- It defers to RARCH_ERR
* Free the ALSA microphone in alsa_free
* Fix an indent
* First draft of alsa_read_microphone
* Deinitialize SDL Audio in sdl_audio_free
* Save and restore the ALSA error logger
- You should always practice safe global state
* Add newlines to some RARCH_ERRs
* Add some logging
* Check for the mic being active via settings instead of via flags
* Adjusted a log entry to be less misleading
- A frequency of 0Hz looks weird to the uninformed
- In reality, it means the driver used the requested frequency
* Fix an incorrect format string
* Tidy up logging in alsa.c
* Rename audio_enable_microphone to audio_enable_input
* Rename microphone_device to audio_input_device
* Add audio_input_latency and audio_input_block_frames settings
* Add all mic-related settings to the options menu
* Adjust logging for alsa.c
- Log the ALSA library version
- Add errno details
* Refer to the microphone in logs by name
* Use %u instead of %d for some log items
* Add input_samples_buf
* Remove an inaccurate comment
* Change type of input_samples_buf
* Clean up audio_driver_flush_microphone_input
* Comment convert_float_to_s16
- It helped me understand what it's doing
- Turns out it'll work just fine on mono audio
* Don't use the resampler for mic input
* Fix crash in the ALSA driver when reading from a mic
* Update some logging messages
* ALSA support now works for mics
* Reuse some common functions in alsa.c
* Add alsa_thread_microphone_t
* Refactor alsa.c
- Introduce alsa_init_pcm to init any PCM that we're using
- Vastly simplifies the implementation of alsa_init and alsa_init_microphone
- Will be used for the read-based versions next
* Make ALSA logging a little more consistent
* Clean up the mic with alsa_free_microphone if alsa_init_microphone fails
* Remove an unused function
* Move some cleanup in alsa.c to a common function
* First crack at mic support for alsathread
- Refactor some duplicate code into functions
- Use functions introduced in alsa.c
- Create and destroy the mic
* Slight cleanups for clarity
* Implement alsa_thread_set/get_microphone_state
* More work on alsathread
- No more crashing, but the mic just returns silence
* Slight cleanups for clarity
* Add alsa_set_mic_enabled_internal
- For setting the state of a microphone while considering its current state
* Use alsa_set_mic_enabled_internal
* Log a little more info
* Log when the audio driver is started/stopped
* Move base microphone driver code into a new directory
- Add microphone_driver.c to Makefile.common
- Rename functions as needed
* Initialize and deinitialize the microphone driver
* Implement sdl_microphone.c
* Un-const an argument
- In case the driver context needs to do any locking
* Revise comments for microphone_driver.h
* Remove an unimplemented function
* Remove some functions from the mic driver
* Remove mic functions from audio_thread_wrapper
* Remove mic functions from sdl_audio
* Fix microphone_null
* Split the mic code for the alsa audio drivers into microphone drivers
* Fix an extra struct member
* Add a setting for the mic driver
* Add a command to reinitialize the microphone driver
* Rename mic-related settings
* Add DRIVER_MICROPHONE_MASK to DRIVERS_CMD_ALL
* Rename audio_enable_input to microphone_enable
* Remove some labels from qt_options
* Search for microphone_driver within find_driver_nonempty
* Clean up some mic driver code
* Pending mics now return silence
* Adjust some logging and comments
* Some cleanup in the microphone driver
* Invert a flag check
- Oops
* Fix a log message
* Fix the wrong flags being checked
* Slight refactor of wasapi_init_device
- Add a data_flow parameter
- Declare it in a header
- In preparation for WASAPI mic support
* Add some WASAPI macros for _IAudioCaptureClient
* Move some common WASAPI functions to audio/common/wasapi.c
- They'll be used by the mic and the audio drivers
* Add wasapi_log_hr
* Generalize mmdevice_list_new to look for capture devices, too
* Fix a function declaration
* Move driver-specific device_list_new functions into their respective files
* Clean up some declarations
* First draft of wasapi microphone driver
* Add wasapi_microphone_device_list_free
* Change function parameter names to be consistent with microphone_driver
* Partially implement wasapi_microphone_read
- Mostly copied from the audio driver so far
- It doesn't compile yet
- But it'll be beautiful when I'm done with it
* Refactor the mic driver's functions
- Rename get_mic_active to mic_alive
- Split set_mic_active into start_mic and stop_mic
- Refactor the SDL mic driver accordingly
* Edit some WASAPI functions for logging and clarity
* Implement more of the WASAPI mic driver
* Rename write_event to read_event
* Pass the WASAPI driver context to the various read functions
* Mostly implement the read function for the WASAPI mic driver
* Fix a crash in microphone_driver
- Forgot to move the position of the name of null_driver
* Reduce some logging in wasapi common functions
- Only log the chosen audio client format, not all attempted ones
* Add some macro wrappers for IAudioClient methods
* Update mic driver configuration
- Make the mic driver configurable in the menu
- Add config items for WASAPI-related options similar to the audio driver
* Fix a menu entry scrolling through audio devices instead of mic devices
* Add some utility functions
* Expose the new utility functions in wasapi.h
* Add extra logging in the WASAPI common functions
* Add sharemode_name
* Use _IAudioClient_Initialize macro in some places
* Pass channels to wasapi_init_client
- Remember, mics are in mono
* Use _IAudioClient_Initialize macro some more
* Forgot to pass channels in some places
* Add some utility functions
* Forgot an #include
* Add wasapi_select_device_format
* Simplify the format selection logic in wasapi_init_client_sh
* Unset the microphone in wasapi_microphone_close_mic
- Ought to prevent a potential segfault
* Simplify some logging
* Fix incorrect value being passed to _IAudioCaptureClient_ReleaseBuffer
* Remove some unneeded logging
* Add some values to hresult_name
* Polish up wasapi_select_device_format
- Test for formats manually when Windows can't
- Add some debug logging
- Check for channels
* Compute the fields of WAVEFORMATEXTENSIBLE correctly
- As per the doc's stated requirements
* Simplify logic for WASAPI client creation
* Fix a potential hang in wasapi_microphone_read_shared_buffered
* Stop the microphone if the driver is stopped
* Don't name the microphone event
* Ensure that wasapi_init_client reports the correct format and rate
* Implement exclusive microphone read access for WASAPI
* Add _IAudioCaptureClient_GetNextPacketSize macro
* Organize cases in hresult_name
* Clear some extra fields if wasapi_set_format is setting a Pcm format
* Adjust some logs
* Adjust some logs
* Remove unneeded local vars
* Add a log
* Update wasapi.c
* Update wasapi.c
* Fix shared-mode mic support in WASAPI producing broken input
- Turns out it had nothing to do with shared mode
* Reuse a common function
- Remove wasapi_microphone_read_shared_buffered
- Rename wasapi_microphone_read_exclusive to wasapi_microphone_read_buffered
* Remove some code I was using for test purposes
* Clarify some language
* Double the default shared-mode mic buffer length
* Split getting a device's name into a separate function, then use it
* Fix the ALSA mic drivers
- To comply with changes I previously made to the mic driver interface
* Remove unused synchronization primitives from the SDL microphone driver
* Add sdl_microphone_mic_use_float
* Document audio_driver_state_flags
- I needed to understand these to see if similar flags were required for the mic driver
* Remove an unused function in wasapi.c
* Add and document flags in microphone_driver.h
* Remove driver-specific mic start/stop functions
- The mic driver itself doesn't do much processing
- That honor goes to individual mics
* Remove some unused fields in microphone_driver.h
* Add CMD_EVENT_MICROPHONE_STOP/START
* Remove unused functions from microphone_null
* Change how the mic driver state is referenced in some places
* Simplify the SDL microphone driver
- The driver backend no longer keeps a reference to the mic (the frontend does that)
- Remove functions that are no longer needed
- Don't track paused state, just query the mic itself
* Simplify the WASAPI microphone driver
- Don't track the driver running state or the microphone handle, the frontend does that now
- Remove support for unbuffered input (hunterk suggested that it wasn't necessary)
* Make microphone_wasapi_sh_buffer_length a uint, not an int
- It won't be negative anymore
- 0 now represents the default value
* Make the microphone frontend more robust
- Improve documentation for how various functions should be implemented
- Closes all microphones before freeing the driver (so backends don't have to)
- Tracks the enabled state of each microphone, so backends don't have to (but they still can)
* Stop the mic driver in core_unload_game
* Ensure mic support is compatible with the revised menu code
* Move alsa.h into audio/common
* Remove RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED
- It was never really needed
* Refactor the ALSA microphone driver
- Move common ALSA functions to audio/common/alsa.c
- Replace alsa_set_mic_enabled_internal with alsa_start/stop_pcm
- Don't track the microphone handle in the ALSA driver context
- Remove unneeded fields
* Move some common alsathread code into audio/common/alsathread.c
* Change return type of mic_driver_open_mic_internal to bool
* First crack at resampling mic input
* Remove an extraneous check
- I think something distracted me when I was writing this line
* Add stereo/mono conversion functions
* Make alsa_start_pcm and alsa_stop_pcm more robust
- They now return success if the stream is already running and stopped, respectively
* Revise some mic-related comments in libretro.h
* First crack at resampling mic input
* Simplify an expression
* Simplify an expression
* Fix a log tag
* Allow mic resampler to be configured separately from audio resampler
* Add some comments
* Set the source ratio to something sensible
* Stop deadlock in `alsathread` mic driver
* Allow mics to be initialized even when core is loaded from CLI
- When loading content from CLI, the drivers are initialized a little differently
- That threw off the mic initialization code
* Rename the functions in retro_microphone_interface
* Revise some mic-related comments in libretro.h
* Update retro_microphone_interface
- Add get_mic_rate
- Add a parameter to open_mic
- The modifications don't do anything yet
* Use parameter objects in the microphone handle
* Replace get_mic_rate with get_params
* Add a microphone interface version
* Remove part of a comment
* Set the effective params in mic_driver_microphone_handle_init
* Drop a stray newline
* Change where the mic interface is zeroed
- I was accidentally throwing out the version that the core was asking for
* Reduce logspam for wasapi_set_nonblock_state
- Now it only logs when the sync mode is changed
* Change DEFAULT_WASAPI_SH_BUFFER_LENGTH to 0
- -16 is no longer a valid value
* Set the new_rate in wasapi_init
* Change description of microphone sample rate in the settings
* First attempt at resampling configured mic input
* Forgot a section
* Fix some input samples being skipped
* Rename a variable for clarity
* Add microphone.outgoing_samples
* Update the mic driver
- Processed samples are now buffered
- The resampler is skipped if the ratio is (very close to) 1
* Remove part of a comment
* Update some comments in audio_resampler.h
* Slightly refactor the SDL microphone driver
- Move SDL_AudioSpec to a field of sdl_microphone_handle_t
- Allow SDL to change the requested format and sample rate
- Request floating-point input
- Implement sdl_microphone_mic_use_float
* Fix a non-C89-compliant declaration
* Add new files to griffin.c
* Remove a C++-style comment
* Add two more files to griffin.c
* Remove some unneeded declarations in microphone_driver.h
* Remove a stray comma in configuration.c
- For C89 compliance
* Fix compilation on some platforms
* Change some function signatures
* Make the ALSA drivers always set the audio rate
* Fix the alsathread mic driver
* Make state_manager_frame_is_reversed return false if HAVE_REWIND isn't defined
* Mute the microphone if the core is running in fast-forward, slow-mo, or rewind
* Clarify a comment
* Clarify a comment
* Add a comment
* Don't allocate memory for slowmo samples in the mic driver
- We're not supporting slowmo for mics, so it's not needed
* Fix a {
* Add my name to AUTHORS.h
* Add driver_lifetime_flags
- For drivers that have special setup/teardown needs
* Ensure that resetting the mic driver maintains active mic handles
- Prevents fullscreen toggle from stopping all mic input
* Update CHANGES.md
* Move some default microphone settings to a new part of the config file
* Ensure that RetroArch can use the audio format that Windows suggests
* Remove references to mic support in the SDL audio driver
* Remove unused WASAPI functions
* Return failure if RetroArch couldn't select a WASAPI format
* Ensure that Windows uses the WASAPI mic driver by default
* Treat disabled mic support as a warning, not an error
* Clarify some WASAPI-related microphone settings
* Remove some unused variables
* Add or revise microphone-related comments
* Rearrange doc comments for microphone types in libretro.h
* Remove a space
* Remove some unused flags
* Remove ALSA error logger
- It was never used anyway
* Remove unneeded microphone-related arguments
* Document a parameter
* Remove a logging call
* Add a constant for the microphone's shared buffer length for WASAPI
* Fix stylistic inconsistencies
* Make mic_driver_get_sample_size a macro instead of a function
* Move the microphone implementation to the audio directory
* Make microphone support optional (but enabled by default)
* Fix the griffin build
2023-06-06 15:55:06 -04:00
|
|
|
driver_uninit(flags, DRIVER_LIFETIME_RESET);
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
memcpy(hwr, &hwr_copy, sizeof(*hwr));
|
|
|
|
video_st->hw_render_context_negotiation = iface;
|
|
|
|
|
Add microphone support via a new driver (#14731)
* Some slight fixes
* Update libretro.h
* Log calls to RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE
* Finish proof-of-concept for mic support
- It works, but doesn't support floating-point audio yet
- It may need to be resampled, too
* Add macros that aren't available in SDL 2
* Comment out a variable definition for now
- For C89 compliance
* Add some comments for clarity
* Let ALSA tolerate a null new_rate
* Partial ALSA microphone support
- Not yet tested
- Mic is created and destroyed
- Mic can also be paused or unpaused
- Mic is paused or unpaused with the rest of the driver
- Microphone is not yet read
* Install error logging in the ALSA driver
- It defers to RARCH_ERR
* Free the ALSA microphone in alsa_free
* Fix an indent
* First draft of alsa_read_microphone
* Deinitialize SDL Audio in sdl_audio_free
* Save and restore the ALSA error logger
- You should always practice safe global state
* Add newlines to some RARCH_ERRs
* Add some logging
* Check for the mic being active via settings instead of via flags
* Adjusted a log entry to be less misleading
- A frequency of 0Hz looks weird to the uninformed
- In reality, it means the driver used the requested frequency
* Fix an incorrect format string
* Tidy up logging in alsa.c
* Rename audio_enable_microphone to audio_enable_input
* Rename microphone_device to audio_input_device
* Add audio_input_latency and audio_input_block_frames settings
* Add all mic-related settings to the options menu
* Adjust logging for alsa.c
- Log the ALSA library version
- Add errno details
* Refer to the microphone in logs by name
* Use %u instead of %d for some log items
* Add input_samples_buf
* Remove an inaccurate comment
* Change type of input_samples_buf
* Clean up audio_driver_flush_microphone_input
* Comment convert_float_to_s16
- It helped me understand what it's doing
- Turns out it'll work just fine on mono audio
* Don't use the resampler for mic input
* Fix crash in the ALSA driver when reading from a mic
* Update some logging messages
* ALSA support now works for mics
* Reuse some common functions in alsa.c
* Add alsa_thread_microphone_t
* Refactor alsa.c
- Introduce alsa_init_pcm to init any PCM that we're using
- Vastly simplifies the implementation of alsa_init and alsa_init_microphone
- Will be used for the read-based versions next
* Make ALSA logging a little more consistent
* Clean up the mic with alsa_free_microphone if alsa_init_microphone fails
* Remove an unused function
* Move some cleanup in alsa.c to a common function
* First crack at mic support for alsathread
- Refactor some duplicate code into functions
- Use functions introduced in alsa.c
- Create and destroy the mic
* Slight cleanups for clarity
* Implement alsa_thread_set/get_microphone_state
* More work on alsathread
- No more crashing, but the mic just returns silence
* Slight cleanups for clarity
* Add alsa_set_mic_enabled_internal
- For setting the state of a microphone while considering its current state
* Use alsa_set_mic_enabled_internal
* Log a little more info
* Log when the audio driver is started/stopped
* Move base microphone driver code into a new directory
- Add microphone_driver.c to Makefile.common
- Rename functions as needed
* Initialize and deinitialize the microphone driver
* Implement sdl_microphone.c
* Un-const an argument
- In case the driver context needs to do any locking
* Revise comments for microphone_driver.h
* Remove an unimplemented function
* Remove some functions from the mic driver
* Remove mic functions from audio_thread_wrapper
* Remove mic functions from sdl_audio
* Fix microphone_null
* Split the mic code for the alsa audio drivers into microphone drivers
* Fix an extra struct member
* Add a setting for the mic driver
* Add a command to reinitialize the microphone driver
* Rename mic-related settings
* Add DRIVER_MICROPHONE_MASK to DRIVERS_CMD_ALL
* Rename audio_enable_input to microphone_enable
* Remove some labels from qt_options
* Search for microphone_driver within find_driver_nonempty
* Clean up some mic driver code
* Pending mics now return silence
* Adjust some logging and comments
* Some cleanup in the microphone driver
* Invert a flag check
- Oops
* Fix a log message
* Fix the wrong flags being checked
* Slight refactor of wasapi_init_device
- Add a data_flow parameter
- Declare it in a header
- In preparation for WASAPI mic support
* Add some WASAPI macros for _IAudioCaptureClient
* Move some common WASAPI functions to audio/common/wasapi.c
- They'll be used by the mic and the audio drivers
* Add wasapi_log_hr
* Generalize mmdevice_list_new to look for capture devices, too
* Fix a function declaration
* Move driver-specific device_list_new functions into their respective files
* Clean up some declarations
* First draft of wasapi microphone driver
* Add wasapi_microphone_device_list_free
* Change function parameter names to be consistent with microphone_driver
* Partially implement wasapi_microphone_read
- Mostly copied from the audio driver so far
- It doesn't compile yet
- But it'll be beautiful when I'm done with it
* Refactor the mic driver's functions
- Rename get_mic_active to mic_alive
- Split set_mic_active into start_mic and stop_mic
- Refactor the SDL mic driver accordingly
* Edit some WASAPI functions for logging and clarity
* Implement more of the WASAPI mic driver
* Rename write_event to read_event
* Pass the WASAPI driver context to the various read functions
* Mostly implement the read function for the WASAPI mic driver
* Fix a crash in microphone_driver
- Forgot to move the position of the name of null_driver
* Reduce some logging in wasapi common functions
- Only log the chosen audio client format, not all attempted ones
* Add some macro wrappers for IAudioClient methods
* Update mic driver configuration
- Make the mic driver configurable in the menu
- Add config items for WASAPI-related options similar to the audio driver
* Fix a menu entry scrolling through audio devices instead of mic devices
* Add some utility functions
* Expose the new utility functions in wasapi.h
* Add extra logging in the WASAPI common functions
* Add sharemode_name
* Use _IAudioClient_Initialize macro in some places
* Pass channels to wasapi_init_client
- Remember, mics are in mono
* Use _IAudioClient_Initialize macro some more
* Forgot to pass channels in some places
* Add some utility functions
* Forgot an #include
* Add wasapi_select_device_format
* Simplify the format selection logic in wasapi_init_client_sh
* Unset the microphone in wasapi_microphone_close_mic
- Ought to prevent a potential segfault
* Simplify some logging
* Fix incorrect value being passed to _IAudioCaptureClient_ReleaseBuffer
* Remove some unneeded logging
* Add some values to hresult_name
* Polish up wasapi_select_device_format
- Test for formats manually when Windows can't
- Add some debug logging
- Check for channels
* Compute the fields of WAVEFORMATEXTENSIBLE correctly
- As per the doc's stated requirements
* Simplify logic for WASAPI client creation
* Fix a potential hang in wasapi_microphone_read_shared_buffered
* Stop the microphone if the driver is stopped
* Don't name the microphone event
* Ensure that wasapi_init_client reports the correct format and rate
* Implement exclusive microphone read access for WASAPI
* Add _IAudioCaptureClient_GetNextPacketSize macro
* Organize cases in hresult_name
* Clear some extra fields if wasapi_set_format is setting a Pcm format
* Adjust some logs
* Adjust some logs
* Remove unneeded local vars
* Add a log
* Update wasapi.c
* Update wasapi.c
* Fix shared-mode mic support in WASAPI producing broken input
- Turns out it had nothing to do with shared mode
* Reuse a common function
- Remove wasapi_microphone_read_shared_buffered
- Rename wasapi_microphone_read_exclusive to wasapi_microphone_read_buffered
* Remove some code I was using for test purposes
* Clarify some language
* Double the default shared-mode mic buffer length
* Split getting a device's name into a separate function, then use it
* Fix the ALSA mic drivers
- To comply with changes I previously made to the mic driver interface
* Remove unused synchronization primitives from the SDL microphone driver
* Add sdl_microphone_mic_use_float
* Document audio_driver_state_flags
- I needed to understand these to see if similar flags were required for the mic driver
* Remove an unused function in wasapi.c
* Add and document flags in microphone_driver.h
* Remove driver-specific mic start/stop functions
- The mic driver itself doesn't do much processing
- That honor goes to individual mics
* Remove some unused fields in microphone_driver.h
* Add CMD_EVENT_MICROPHONE_STOP/START
* Remove unused functions from microphone_null
* Change how the mic driver state is referenced in some places
* Simplify the SDL microphone driver
- The driver backend no longer keeps a reference to the mic (the frontend does that)
- Remove functions that are no longer needed
- Don't track paused state, just query the mic itself
* Simplify the WASAPI microphone driver
- Don't track the driver running state or the microphone handle, the frontend does that now
- Remove support for unbuffered input (hunterk suggested that it wasn't necessary)
* Make microphone_wasapi_sh_buffer_length a uint, not an int
- It won't be negative anymore
- 0 now represents the default value
* Make the microphone frontend more robust
- Improve documentation for how various functions should be implemented
- Closes all microphones before freeing the driver (so backends don't have to)
- Tracks the enabled state of each microphone, so backends don't have to (but they still can)
* Stop the mic driver in core_unload_game
* Ensure mic support is compatible with the revised menu code
* Move alsa.h into audio/common
* Remove RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED
- It was never really needed
* Refactor the ALSA microphone driver
- Move common ALSA functions to audio/common/alsa.c
- Replace alsa_set_mic_enabled_internal with alsa_start/stop_pcm
- Don't track the microphone handle in the ALSA driver context
- Remove unneeded fields
* Move some common alsathread code into audio/common/alsathread.c
* Change return type of mic_driver_open_mic_internal to bool
* First crack at resampling mic input
* Remove an extraneous check
- I think something distracted me when I was writing this line
* Add stereo/mono conversion functions
* Make alsa_start_pcm and alsa_stop_pcm more robust
- They now return success if the stream is already running and stopped, respectively
* Revise some mic-related comments in libretro.h
* First crack at resampling mic input
* Simplify an expression
* Simplify an expression
* Fix a log tag
* Allow mic resampler to be configured separately from audio resampler
* Add some comments
* Set the source ratio to something sensible
* Stop deadlock in `alsathread` mic driver
* Allow mics to be initialized even when core is loaded from CLI
- When loading content from CLI, the drivers are initialized a little differently
- That threw off the mic initialization code
* Rename the functions in retro_microphone_interface
* Revise some mic-related comments in libretro.h
* Update retro_microphone_interface
- Add get_mic_rate
- Add a parameter to open_mic
- The modifications don't do anything yet
* Use parameter objects in the microphone handle
* Replace get_mic_rate with get_params
* Add a microphone interface version
* Remove part of a comment
* Set the effective params in mic_driver_microphone_handle_init
* Drop a stray newline
* Change where the mic interface is zeroed
- I was accidentally throwing out the version that the core was asking for
* Reduce logspam for wasapi_set_nonblock_state
- Now it only logs when the sync mode is changed
* Change DEFAULT_WASAPI_SH_BUFFER_LENGTH to 0
- -16 is no longer a valid value
* Set the new_rate in wasapi_init
* Change description of microphone sample rate in the settings
* First attempt at resampling configured mic input
* Forgot a section
* Fix some input samples being skipped
* Rename a variable for clarity
* Add microphone.outgoing_samples
* Update the mic driver
- Processed samples are now buffered
- The resampler is skipped if the ratio is (very close to) 1
* Remove part of a comment
* Update some comments in audio_resampler.h
* Slightly refactor the SDL microphone driver
- Move SDL_AudioSpec to a field of sdl_microphone_handle_t
- Allow SDL to change the requested format and sample rate
- Request floating-point input
- Implement sdl_microphone_mic_use_float
* Fix a non-C89-compliant declaration
* Add new files to griffin.c
* Remove a C++-style comment
* Add two more files to griffin.c
* Remove some unneeded declarations in microphone_driver.h
* Remove a stray comma in configuration.c
- For C89 compliance
* Fix compilation on some platforms
* Change some function signatures
* Make the ALSA drivers always set the audio rate
* Fix the alsathread mic driver
* Make state_manager_frame_is_reversed return false if HAVE_REWIND isn't defined
* Mute the microphone if the core is running in fast-forward, slow-mo, or rewind
* Clarify a comment
* Clarify a comment
* Add a comment
* Don't allocate memory for slowmo samples in the mic driver
- We're not supporting slowmo for mics, so it's not needed
* Fix a {
* Add my name to AUTHORS.h
* Add driver_lifetime_flags
- For drivers that have special setup/teardown needs
* Ensure that resetting the mic driver maintains active mic handles
- Prevents fullscreen toggle from stopping all mic input
* Update CHANGES.md
* Move some default microphone settings to a new part of the config file
* Ensure that RetroArch can use the audio format that Windows suggests
* Remove references to mic support in the SDL audio driver
* Remove unused WASAPI functions
* Return failure if RetroArch couldn't select a WASAPI format
* Ensure that Windows uses the WASAPI mic driver by default
* Treat disabled mic support as a warning, not an error
* Clarify some WASAPI-related microphone settings
* Remove some unused variables
* Add or revise microphone-related comments
* Rearrange doc comments for microphone types in libretro.h
* Remove a space
* Remove some unused flags
* Remove ALSA error logger
- It was never used anyway
* Remove unneeded microphone-related arguments
* Document a parameter
* Remove a logging call
* Add a constant for the microphone's shared buffer length for WASAPI
* Fix stylistic inconsistencies
* Make mic_driver_get_sample_size a macro instead of a function
* Move the microphone implementation to the audio directory
* Make microphone support optional (but enabled by default)
* Fix the griffin build
2023-06-06 15:55:06 -04:00
|
|
|
drivers_init(settings, flags, DRIVER_LIFETIME_RESET, verbosity_is_enabled());
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_driver_reinit(int flags)
|
|
|
|
{
|
|
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
video_driver_state_t *video_st = &video_driver_st;
|
|
|
|
struct retro_hw_render_callback *hwr =
|
2022-12-22 19:58:26 +02:00
|
|
|
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
|
2023-07-20 13:29:46 +03:00
|
|
|
|
2022-10-08 22:52:18 +02:00
|
|
|
if (hwr->cache_context != false)
|
2022-12-22 19:58:26 +02:00
|
|
|
video_st->flags |= VIDEO_FLAG_CACHE_CONTEXT;
|
2022-10-08 22:52:18 +02:00
|
|
|
else
|
2022-12-22 19:58:26 +02:00
|
|
|
video_st->flags &= ~VIDEO_FLAG_CACHE_CONTEXT;
|
2022-11-23 20:55:05 +01:00
|
|
|
video_st->flags &= ~VIDEO_FLAG_CACHE_CONTEXT_ACK;
|
2022-10-08 22:52:18 +02:00
|
|
|
video_driver_reinit_context(settings, flags);
|
2022-11-23 20:55:05 +01:00
|
|
|
video_st->flags &= ~VIDEO_FLAG_CACHE_CONTEXT;
|
2023-07-20 13:29:46 +03:00
|
|
|
|
|
|
|
video_st->window_title_prev[0] = '\0';
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_frame_delay_auto(video_driver_state_t *video_st, video_frame_delay_auto_t *vfda)
|
|
|
|
{
|
2023-01-05 19:44:12 +01:00
|
|
|
int i;
|
2023-01-09 00:05:21 +01:00
|
|
|
retro_time_t frame_time_avg = 0;
|
|
|
|
retro_time_t frame_time_delta = 0;
|
|
|
|
uint16_t frame_time_index = (video_st->frame_time_count & (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1));
|
|
|
|
retro_time_t frame_time_target = 1000000.0f / vfda->refresh_rate;
|
|
|
|
retro_time_t frame_time_limit_mar = frame_time_target * 1.15f;
|
|
|
|
retro_time_t frame_time_limit_min = frame_time_target * 1.30f;
|
|
|
|
retro_time_t frame_time_limit_med = frame_time_target * 1.50f;
|
|
|
|
retro_time_t frame_time_limit_max = frame_time_target * 1.85f;
|
|
|
|
retro_time_t frame_time_limit_cap = frame_time_target * 3.00f;
|
|
|
|
retro_time_t frame_time_limit_ign = frame_time_target * 3.25f;
|
|
|
|
uint8_t frame_time_frames = vfda->frame_time_interval;
|
|
|
|
uint8_t frame_time_count_pos = 0;
|
|
|
|
uint8_t frame_time_count_min = 0;
|
|
|
|
uint8_t frame_time_count_med = 0;
|
|
|
|
uint8_t frame_time_count_max = 0;
|
|
|
|
uint8_t frame_time_count_ign = 0;
|
|
|
|
retro_time_t frame_time_min = frame_time_target;
|
|
|
|
retro_time_t frame_time_max = frame_time_target;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Calculate average frame time */
|
|
|
|
for (i = 1; i < frame_time_frames + 1; i++)
|
|
|
|
{
|
2023-01-09 00:05:21 +01:00
|
|
|
retro_time_t frame_time_i = 0;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
if (i > frame_time_index)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
frame_time_i = video_st->frame_time_samples[frame_time_index - i];
|
|
|
|
|
|
|
|
if (frame_time_max < frame_time_i)
|
|
|
|
frame_time_max = frame_time_i;
|
|
|
|
if (frame_time_min > frame_time_i)
|
|
|
|
frame_time_min = frame_time_i;
|
|
|
|
|
|
|
|
/* Count frames over the target */
|
|
|
|
if (frame_time_i > frame_time_target)
|
|
|
|
{
|
|
|
|
frame_time_count_pos++;
|
|
|
|
if (frame_time_i > frame_time_limit_min)
|
|
|
|
frame_time_count_min++;
|
|
|
|
if (frame_time_i > frame_time_limit_med)
|
|
|
|
frame_time_count_med++;
|
|
|
|
if (frame_time_i > frame_time_limit_max)
|
|
|
|
frame_time_count_max++;
|
|
|
|
if (frame_time_i > frame_time_limit_ign)
|
|
|
|
frame_time_count_ign++;
|
|
|
|
|
|
|
|
/* Limit maximum to prevent false positives */
|
|
|
|
if (frame_time_i > frame_time_limit_cap)
|
|
|
|
frame_time_i = frame_time_limit_cap;
|
|
|
|
}
|
|
|
|
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg += frame_time_i;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg /= frame_time_frames;
|
2022-12-22 19:58:26 +02:00
|
|
|
frame_time_delta = frame_time_max - frame_time_min;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Ignore values when core is doing internal frame skipping */
|
|
|
|
if (frame_time_count_ign > 0)
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg = frame_time_target;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Special handlings for different video driver frame timings */
|
2023-01-09 00:05:21 +01:00
|
|
|
if ( ( frame_time_avg > frame_time_target
|
|
|
|
&& frame_time_avg < frame_time_limit_med)
|
2022-12-22 19:58:26 +02:00
|
|
|
|| (frame_time_delta > frame_time_limit_max))
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2022-12-22 19:58:26 +02:00
|
|
|
uint8_t frame_time_frames_half = frame_time_frames / 2;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Ensure outcome on certain conditions */
|
2022-12-22 19:58:26 +02:00
|
|
|
int8_t mode = 0;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
2022-12-22 19:58:26 +02:00
|
|
|
/* All interval frames are above the target */
|
2022-10-08 22:52:18 +02:00
|
|
|
if (frame_time_count_pos == frame_time_frames)
|
|
|
|
mode = 1;
|
|
|
|
/* At least half of interval frames are above minimum level */
|
|
|
|
else if (frame_time_count_min >= frame_time_frames_half)
|
|
|
|
mode = 2;
|
|
|
|
/* D3Dx stripe equalizer */
|
|
|
|
else if (
|
2022-12-22 19:58:26 +02:00
|
|
|
(frame_time_count_pos == frame_time_frames_half)
|
|
|
|
&& ( frame_time_count_min > 0
|
|
|
|
|| frame_time_count_med > 0)
|
|
|
|
&& (frame_time_delta > frame_time_target / 2.50f)
|
2022-10-08 22:52:18 +02:00
|
|
|
)
|
|
|
|
mode = 3;
|
|
|
|
/* Boost med/max spikes */
|
|
|
|
else if (
|
2022-12-22 19:58:26 +02:00
|
|
|
(frame_time_count_pos > frame_time_frames_half)
|
|
|
|
&& ( frame_time_count_med > 1
|
|
|
|
|| frame_time_count_max > 0)
|
2023-01-09 00:05:21 +01:00
|
|
|
&& (frame_time_avg > frame_time_limit_mar)
|
2022-12-22 19:58:26 +02:00
|
|
|
&& (frame_time_delta < frame_time_limit_min)
|
2022-10-08 22:52:18 +02:00
|
|
|
)
|
|
|
|
mode = 4;
|
2022-12-22 19:58:26 +02:00
|
|
|
/* Boost min/med spikes */
|
|
|
|
else if (
|
|
|
|
(frame_time_count_pos >= frame_time_frames_half)
|
|
|
|
&& ( frame_time_count_min > 1
|
|
|
|
|| frame_time_count_med > 0)
|
|
|
|
&& (frame_time_count_max == 0)
|
2023-01-09 00:05:21 +01:00
|
|
|
&& (frame_time_avg > frame_time_limit_mar)
|
2022-12-22 19:58:26 +02:00
|
|
|
)
|
|
|
|
mode = 5;
|
2022-10-08 22:52:18 +02:00
|
|
|
/* Ignore */
|
|
|
|
else if (
|
2022-11-23 20:55:05 +01:00
|
|
|
(frame_time_delta > frame_time_target)
|
|
|
|
&& (frame_time_count_med == 0)
|
2022-10-08 22:52:18 +02:00
|
|
|
)
|
|
|
|
mode = -1;
|
|
|
|
|
2023-01-09 00:05:21 +01:00
|
|
|
if (mode > 0 && frame_time_avg < frame_time_limit_med)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
#if FRAME_DELAY_AUTO_DEBUG
|
2023-01-09 00:05:21 +01:00
|
|
|
RARCH_LOG("[Video]: Frame delay nudge %d by mode %d.\n", frame_time_avg, mode);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg = frame_time_limit_med;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
else if (mode < 0)
|
|
|
|
{
|
|
|
|
#if FRAME_DELAY_AUTO_DEBUG
|
2023-01-09 00:05:21 +01:00
|
|
|
RARCH_LOG("[Video]: Frame delay ignore %d.\n", frame_time_avg);
|
2022-10-08 22:52:18 +02:00
|
|
|
#endif
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg = frame_time_target;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Final output decision */
|
2023-01-09 00:05:21 +01:00
|
|
|
if (frame_time_avg > frame_time_limit_min)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
2022-12-22 19:58:26 +02:00
|
|
|
uint8_t delay_decrease = 1;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
/* Increase decrease the more frame time is off target */
|
2023-01-09 00:05:21 +01:00
|
|
|
if ( frame_time_avg > frame_time_limit_med
|
2022-12-22 19:58:26 +02:00
|
|
|
&& video_st->frame_delay_effective > delay_decrease)
|
2022-10-08 22:52:18 +02:00
|
|
|
{
|
|
|
|
delay_decrease++;
|
2023-01-09 00:05:21 +01:00
|
|
|
if ( frame_time_avg > frame_time_limit_max
|
2022-12-22 19:58:26 +02:00
|
|
|
&& video_st->frame_delay_effective > delay_decrease)
|
2022-10-08 22:52:18 +02:00
|
|
|
delay_decrease++;
|
|
|
|
}
|
|
|
|
|
2022-12-22 19:58:26 +02:00
|
|
|
vfda->delay_decrease = delay_decrease;
|
2022-10-08 22:52:18 +02:00
|
|
|
}
|
|
|
|
|
2023-01-09 00:05:21 +01:00
|
|
|
vfda->frame_time_avg = frame_time_avg;
|
2022-12-22 19:58:26 +02:00
|
|
|
vfda->frame_time_target = frame_time_target;
|
2022-10-08 22:52:18 +02:00
|
|
|
|
|
|
|
#if FRAME_DELAY_AUTO_DEBUG
|
|
|
|
if (frame_time_index > frame_time_frames)
|
2022-12-22 19:58:26 +02:00
|
|
|
RARCH_LOG("[Video]: %5d / pos:%d min:%d med:%d max:%d ign:%d / delta:%5d = %5d %5d %5d %5d %5d %5d %5d %5d\n",
|
2023-01-09 00:05:21 +01:00
|
|
|
frame_time_avg,
|
2022-10-08 22:52:18 +02:00
|
|
|
frame_time_count_pos,
|
|
|
|
frame_time_count_min,
|
|
|
|
frame_time_count_med,
|
|
|
|
frame_time_count_max,
|
2022-12-22 19:58:26 +02:00
|
|
|
frame_time_count_ign,
|
2022-10-08 22:52:18 +02:00
|
|
|
frame_time_max - frame_time_min,
|
|
|
|
video_st->frame_time_samples[frame_time_index - 1],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 2],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 3],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 4],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 5],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 6],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 7],
|
|
|
|
video_st->frame_time_samples[frame_time_index - 8]
|
|
|
|
);
|
|
|
|
#endif
|
|
|
|
}
|