Adds auto frameskip based on free audio buffer space
[pcsx_rearmed.git] / frontend / libretro.c
index e9567de..255ba3a 100644 (file)
@@ -96,6 +96,18 @@ static int show_advanced_gpu_unai_settings = -1;
 static int show_other_input_settings = -1;
 static float mouse_sensitivity = 1.0f;
 
+unsigned frameskip_type                  = 0;
+unsigned frameskip_threshold             = 0;
+unsigned frameskip_counter               = 0;
+unsigned frameskip_interval              = 0;
+
+int retro_audio_buff_active              = false;
+unsigned retro_audio_buff_occupancy      = 0;
+int retro_audio_buff_underrun            = false;
+
+unsigned retro_audio_latency             = 0;
+int update_audio_latency                 = false;
+
 static unsigned previous_width = 0;
 static unsigned previous_height = 0;
 
@@ -121,7 +133,6 @@ int in_mouse[8][2];
 int multitap1 = 0;
 int multitap2 = 0;
 int in_enable_vibration = 1;
-static int input_changed = 0;
 
 // NegCon adjustment parameters
 // > The NegCon 'twist' action is somewhat awkward when mapped
@@ -592,37 +603,24 @@ unsigned retro_api_version(void)
 
 static void update_multitap(void)
 {
-   struct retro_variable var = {};
+   struct retro_variable var = { 0 };
+
+   multitap1 = 0;
+   multitap2 = 0;
 
    var.value = NULL;
    var.key = "pcsx_rearmed_multitap";
    if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value))
    {
-      if (strcmp(var.value, "port 1 only") == 0)
-      {
+      if (strcmp(var.value, "port 1") == 0)
          multitap1 = 1;
-         multitap2 = 0;
-      }
-      else if (strcmp(var.value, "port 2 only") == 0)
-      {
-         multitap1 = 0;
+      else if (strcmp(var.value, "port 2") == 0)
          multitap2 = 1;
-      }
-      else if (strcmp(var.value, "both") == 0)
+      else if (strcmp(var.value, "ports 1 and 2") == 0)
       {
          multitap1 = 1;
          multitap2 = 1;
       }
-      else
-      {
-         multitap1 = 0;
-         multitap2 = 0;
-      }
-   }
-   else
-   {
-      multitap1 = 0;
-      multitap2 = 0;
    }
 }
 
@@ -659,7 +657,6 @@ void retro_set_controller_port_device(unsigned port, unsigned device)
    }
 
    SysPrintf("port: %u  device: %s\n", port + 1, get_pse_pad_label[in_type[port]]);
-   input_changed = 1;
 }
 
 void retro_get_system_info(struct retro_system_info *info)
@@ -669,7 +666,7 @@ void retro_get_system_info(struct retro_system_info *info)
 #endif
    memset(info, 0, sizeof(*info));
    info->library_name     = "PCSX-ReARMed";
-   info->library_version  = "r22" GIT_VERSION;
+   info->library_version  = "r23l" GIT_VERSION;
    info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u|chd";
    info->need_fullpath    = true;
 }
@@ -1210,6 +1207,52 @@ static void set_retro_memmap(void)
 #endif
 }
 
+static void retro_audio_buff_status_cb(
+   bool active, unsigned occupancy, bool underrun_likely)
+{
+   retro_audio_buff_active    = active;
+   retro_audio_buff_occupancy = occupancy;
+   retro_audio_buff_underrun  = underrun_likely;
+}
+
+static void retro_set_audio_buff_status_cb(void)
+{
+   if (frameskip_type > 0)
+   {
+      struct retro_audio_buffer_status_callback buf_status_cb;
+
+      buf_status_cb.callback = retro_audio_buff_status_cb;
+      if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
+            &buf_status_cb))
+      {
+         retro_audio_buff_active    = false;
+         retro_audio_buff_occupancy = 0;
+         retro_audio_buff_underrun  = false;
+         retro_audio_latency        = 0;
+      }
+      else
+      {
+         /* Frameskip is enabled - increase frontend
+          * audio latency to minimise potential
+          * buffer underruns */
+         uint32_t frame_time_usec = 1000000.0 / (is_pal_mode ? 50.0 : 60.0);
+
+         /* Set latency to 6x current frame time... */
+         retro_audio_latency = (unsigned)(6 * frame_time_usec / 1000);
+
+         /* ...then round up to nearest multiple of 32 */
+         retro_audio_latency = (retro_audio_latency + 0x1F) & ~0x1F;
+      }
+   }
+   else
+   {
+      environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
+      retro_audio_latency = 0;
+   }
+
+   update_audio_latency = true;
+}
+
 static void update_variables(bool in_flight);
 bool retro_load_game(const struct retro_game_info *info)
 {
@@ -1352,14 +1395,12 @@ bool retro_load_game(const struct retro_game_info *info)
 
       for (i = 0; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++)
       {
-         char disk_name[PATH_MAX];
-         char disk_label[PATH_MAX];
-         disk_name[0] = '\0';
-         disk_label[0] = '\0';
+         char disk_name[PATH_MAX - 16] = { 0 };
+         char disk_label[PATH_MAX] = { 0 };
 
          disks[i].fname = strdup(info->path);
 
-         get_disk_label(disk_name, info->path, PATH_MAX);
+         get_disk_label(disk_name, info->path, sizeof(disk_name));
          snprintf(disk_label, sizeof(disk_label), "%s #%u", disk_name, (unsigned)i + 1);
          disks[i].flabel = strdup(disk_label);
 
@@ -1400,6 +1441,10 @@ bool retro_load_game(const struct retro_game_info *info)
       }
    }
 
+   /* set ports to use "standard controller" initially */
+   for (i = 0; i < 8; ++i)
+      in_type[i] = PSE_PAD_TYPE_STANDARD;
+
    plugin_call_rearmed_cbs();
    /* dfinput_activate(); */
 
@@ -1419,8 +1464,7 @@ bool retro_load_game(const struct retro_game_info *info)
    emu_on_new_cd(0);
 
    set_retro_memmap();
-
-   input_changed = 1;
+   retro_set_audio_buff_status_cb();
 
    return true;
 }
@@ -1491,11 +1535,43 @@ static void update_variables(bool in_flight)
 #ifdef GPU_PEOPS
    int gpu_peops_fix = 0;
 #endif
+   unsigned prev_frameskip_type;
+
+   var.key = "pcsx_rearmed_frameskip_type";
+   var.value = NULL;
+
+   prev_frameskip_type = frameskip_type;
+   frameskip_type = 0;
+   pl_rearmed_cbs.frameskip = 0;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "auto") == 0)
+         frameskip_type = 1;
+      if (strcmp(var.value, "auto_threshold") == 0)
+         frameskip_type = 2;
+      if (strcmp(var.value, "fixed_interval") == 0)
+         frameskip_type = 3;
+   }
+
+   if (frameskip_type != 0)
+      pl_rearmed_cbs.frameskip = -1;
 
+   var.key = "pcsx_rearmed_frameskip_threshold";
    var.value = NULL;
+
+   frameskip_threshold = 30;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+      frameskip_threshold = strtol(var.value, NULL, 10);
+
    var.key = "pcsx_rearmed_frameskip";
+   var.value = NULL;
+
+   frameskip_interval = 1;
+
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
-      pl_rearmed_cbs.frameskip = atoi(var.value);
+      frameskip_interval = strtol(var.value, NULL, 10);
 
    var.value = NULL;
    var.key = "pcsx_rearmed_region";
@@ -1510,9 +1586,6 @@ static void update_variables(bool in_flight)
          Config.PsxType = 1;
    }
 
-   /*for (i = 0; i < PORTS_NUMBER; i++)
-      update_controller_port_variable(i);*/
-
    update_multitap();
 
    var.value = NULL;
@@ -1646,11 +1719,13 @@ static void update_variables(bool in_flight)
          display_internal_fps = true;
    }
 
-#if defined(LIGHTREC) || defined(NEW_DYNAREC)
+#ifndef DRC_DISABLE
    var.value = NULL;
    var.key = "pcsx_rearmed_drc";
 
-   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   if (!environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
+      var.value = "enabled";
+
    {
       R3000Acpu *prev_cpu = psxCpu;
 #if defined(LIGHTREC)
@@ -1677,7 +1752,8 @@ static void update_variables(bool in_flight)
          psxCpu->Reset(); // not really a reset..
       }
    }
-#endif /* LIGHTREC || NEW_DYNAREC */
+#endif /* !DRC_DISABLE */
+   psxCpu->ApplyConfig();
 
    var.value = NULL;
    var.key = "pcsx_rearmed_spu_reverb";
@@ -1715,16 +1791,16 @@ static void update_variables(bool in_flight)
       else if (strcmp(var.value, "enabled") == 0)
          Config.RCntFix = 1;
    }
-
+   
    var.value = NULL;
-   var.key = "pcsx_rearmed_idiablofix";
+   var.key = "pcsx_rearmed_icache_emulation";
 
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
    {
       if (strcmp(var.value, "disabled") == 0)
-         spu_config.idiablofix = 0;
+         Config.icache_emulation = 0;
       else if (strcmp(var.value, "enabled") == 0)
-         spu_config.idiablofix = 1;
+         Config.icache_emulation = 1;
    }
 
    var.value = NULL;
@@ -2068,7 +2144,7 @@ static void update_variables(bool in_flight)
       GunconAdjustRatioY = atof(var.value);
    }
 
-#ifdef NEW_DYNAREC
+#if !defined(DRC_DISABLE) && !defined(LIGHTREC)
    var.value = NULL;
    var.key = "pcsx_rearmed_nosmccheck";
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
@@ -2107,7 +2183,27 @@ static void update_variables(bool in_flight)
       int psxclock = atoi(var.value);
       cycle_multiplier = 10000 / psxclock;
    }
-#endif /* NEW_DYNAREC */
+
+   var.value = NULL;
+   var.key = "pcsx_rearmed_nocompathacks";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         new_dynarec_hacks |= NDHACK_NO_COMPAT_HACKS;
+      else
+         new_dynarec_hacks &= ~NDHACK_NO_COMPAT_HACKS;
+   }
+#endif /* !DRC_DISABLE && !LIGHTREC */
+
+   var.value = NULL;
+   var.key = "pcsx_rearmed_nostalls";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         Config.DisableStalls = 1;
+      else
+         Config.DisableStalls = 0;
+   }
 
    var.value = NULL;
    var.key = "pcsx_rearmed_input_sensitivity";
@@ -2163,6 +2259,10 @@ static void update_variables(bool in_flight)
          GPU_open(&gpuDisp, "PCSX", NULL);
       }
 
+      /* Reinitialise frameskipping, if required */
+      if (((frameskip_type     != prev_frameskip_type)))
+         retro_set_audio_buff_status_cb();
+
       /* dfinput_activate(); */
    }
    else
@@ -2520,19 +2620,6 @@ static void print_internal_fps(void)
 
 void retro_run(void)
 {
-   /* update multitap when inputs have changed */
-   /* this is only applied on core restart */
-   if (input_changed)
-   {
-      int i;
-      input_changed = 0;
-      update_multitap();
-      for (i = 0; i < 8; i++)
-         SysDLog("Player %d: %s\n", i + 1, get_pse_pad_label[in_type[i]]);
-      SysDLog("Multiplayer 1: %s\n", multitap1 ? "enabled" : "disabled");
-      SysDLog("Multiplayer 2: %s\n", multitap2 ? "enabled" : "disabled");
-   }
-
    //SysReset must be run while core is running,Not in menu (Locks up Retroarch)
    if (rebootemu != 0)
    {
@@ -2548,6 +2635,46 @@ void retro_run(void)
 
    print_internal_fps();
 
+   /* Check whether current frame should
+    * be skipped */
+   pl_rearmed_cbs.fskip_force = 0;
+   pl_rearmed_cbs.fskip_dirty = 0;
+   if ((frameskip_type > 0) && retro_audio_buff_active)
+   {
+      bool skip_frame;
+
+      switch (frameskip_type)
+      {
+         case 1: /* auto */
+            skip_frame = retro_audio_buff_underrun;
+            break;
+         case 2: /* threshold */
+            skip_frame = (retro_audio_buff_occupancy < frameskip_threshold);
+            break;
+         case 3: /* fixed */
+            skip_frame = true;
+            break;
+         default:
+            skip_frame = false;
+            break;
+      }
+
+      if (skip_frame && frameskip_counter < frameskip_interval)
+         pl_rearmed_cbs.fskip_force = 1;
+   }
+
+   /* If frameskip/timing settings have changed,
+    * update frontend audio latency
+    * > Can do this before or after the frameskip
+    *   check, but doing it after means we at least
+    *   retain the current frame's audio output */
+   if (update_audio_latency)
+   {
+      environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
+            &retro_audio_latency);
+      update_audio_latency = false;
+   }
+
    input_poll_cb();
 
    update_input();
@@ -2559,6 +2686,13 @@ void retro_run(void)
    stop = 0;
    psxCpu->Execute();
 
+   if (pl_rearmed_cbs.fskip_dirty == 1) {
+      if (frameskip_counter < frameskip_interval)
+         frameskip_counter++;
+      else if (frameskip_counter >= frameskip_interval || !pl_rearmed_cbs.fskip_force)
+         frameskip_counter = 0;
+   }
+
    video_cb((vout_fb_dirty || !vout_can_dupe || !duping_enable) ? vout_buf_ptr : NULL,
        vout_width, vout_height, vout_width * 2);
    vout_fb_dirty = 0;
@@ -2678,7 +2812,6 @@ static void loadPSXBios(void)
    unsigned useHLE = 0;
 
    const char *bios[] = {
-      "PS1_ROM", "ps1_rom",
       "PSXONPSP660", "psxonpsp660",
       "SCPH101", "scph101",
       "SCPH5501", "scph5501",
@@ -2803,7 +2936,8 @@ void retro_init(void)
 #ifdef _3DS
    vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
 #elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && !defined(VITA) && !defined(__SWITCH__)
-   posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
+   if (posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2) != 0)
+      vout_buf = (void *) 0;
 #else
    vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
 #endif
@@ -2871,6 +3005,14 @@ void retro_deinit(void)
    /* Have to reset disks struct, otherwise
     * fnames/flabels will leak memory */
    disk_init();
+   frameskip_type             = 0;
+   frameskip_threshold        = 0;
+   frameskip_counter          = 0;
+   retro_audio_buff_active    = false;
+   retro_audio_buff_occupancy = 0;
+   retro_audio_buff_underrun  = false;
+   retro_audio_latency        = 0;
+   update_audio_latency       = false;
 }
 
 #ifdef VITA