Esempio n. 1
0
static void DVDThread()
{
  Common::SetCurrentThreadName("DVD thread");

  while (true)
  {
    s_request_queue_expanded.Wait();

    if (s_dvd_thread_exiting.IsSet())
      return;

    ReadRequest request;
    while (s_request_queue.Pop(request))
    {
      FileMonitor::Log(*s_disc, request.partition, request.dvd_offset);

      std::vector<u8> buffer(request.length);
      if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
        buffer.resize(0);

      request.realtime_done_us = Common::Timer::GetTimeUs();

      s_result_queue.Push(ReadResult(std::move(request), std::move(buffer)));
      s_result_queue_expanded.Set();

      if (s_dvd_thread_exiting.IsSet())
        return;
    }
  }
}
Esempio n. 2
0
u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData)
{
	if (s_BackendInitialized && g_ActiveConfig.bEFBAccessEnable)
	{
		s_accessEFBArgs.type = type;
		s_accessEFBArgs.x = x;
		s_accessEFBArgs.y = y;
		s_accessEFBArgs.Data = InputData;

		s_efbAccessRequested.Set();

		if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread)
		{
			s_efbAccessReadyEvent.Reset();
			if (s_FifoShuttingDown.IsSet())
				return 0;
			s_efbAccessRequested.Set();
			s_efbAccessReadyEvent.Wait();
		}
		else
			VideoFifo_CheckEFBAccess();

		return s_AccessEFBResult;
	}

	return 0;
}
Esempio n. 3
0
 virtual void MainLoop()
 {
   while (s_running.IsSet())
   {
     Core::HostDispatchJobs();
     std::this_thread::sleep_for(std::chrono::milliseconds(100));
   }
 }
Esempio n. 4
0
static void VideoFifo_CheckPerfQueryRequest()
{
	if (s_perfQueryRequested.IsSet())
	{
		g_perf_query->FlushResults();
		s_perfQueryRequested.Clear();
		s_perfQueryReadyEvent.Set();
	}
}
Esempio n. 5
0
void VideoFifo_CheckEFBAccess()
{
	if (s_efbAccessRequested.IsSet())
	{
		s_AccessEFBResult = g_renderer->AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y, s_accessEFBArgs.Data);
		s_efbAccessRequested.Clear();
		s_efbAccessReadyEvent.Set();
	}
}
Esempio n. 6
0
// Run from the graphics thread (from Fifo.cpp)
static void VideoFifo_CheckSwapRequest()
{
	if (g_ActiveConfig.bUseXFB)
	{
		if (s_swapRequested.IsSet())
		{
			EFBRectangle rc;
			Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbHeight,rc);
			s_swapRequested.Clear();
		}
	}
}
Esempio n. 7
0
// Run from the graphics thread (from Fifo.cpp)
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
	if (g_ActiveConfig.bUseXFB)
	{
		if (s_swapRequested.IsSet())
		{
			u32 aLower = xfbAddr;
			u32 aUpper = xfbAddr + 2 * fbWidth * fbHeight;
			u32 bLower = s_beginFieldArgs.xfbAddr;
			u32 bUpper = s_beginFieldArgs.xfbAddr + 2 * s_beginFieldArgs.fbWidth * s_beginFieldArgs.fbHeight;

			if (addrRangesOverlap(aLower, aUpper, bLower, bUpper))
				VideoFifo_CheckSwapRequest();
		}
	}
}
Esempio n. 8
0
void HiresTexture::Prefetch()
{
	Common::SetCurrentThreadName("Prefetcher");

	u32 starttime = Common::Timer::GetTimeMs();
	for (const auto& entry : s_textureMap)
	{
		const std::string& base_filename = entry.first;

		std::unique_lock<std::mutex> lk(s_textureCacheMutex);

		auto iter = s_textureCache.find(base_filename);
		if (iter == s_textureCache.end())
		{
			lk.unlock();
			HiresTexture* ptr = Load(base_filename, [](size_t requested_size)
			{
				return new u8[requested_size];
			}, true);
			lk.lock();
			if (ptr != nullptr)
			{
				size_sum.fetch_add(ptr->m_cached_data_size);
				iter = s_textureCache.insert(iter, std::make_pair(base_filename, std::shared_ptr<HiresTexture>(ptr)));
			}
		}

		if (s_textureCacheAbortLoading.IsSet())
		{
			return;
		}

		if (size_sum.load() > max_mem)
		{
			g_Config.bCacheHiresTextures = false;

			OSD::AddMessage(StringFromFormat("Custom Textures prefetching after %.1f MB aborted, not enough RAM available", size_sum / (1024.0 * 1024.0)), 10000);
			return;
		}
	}
	u32 stoptime = Common::Timer::GetTimeMs();
	OSD::AddMessage(StringFromFormat("Custom Textures loaded, %.1f MB in %.1f s", size_sum / (1024.0 * 1024.0), (stoptime - starttime) / 1000.0), 10000);
}
Esempio n. 9
0
u32 VideoBackendHardware::Video_GetQueryResult(PerfQueryType type)
{
	if (!g_perf_query->ShouldEmulate())
	{
		return 0;
	}

	// TODO: Is this check sane?
	if (!g_perf_query->IsFlushed())
	{
		if (SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread)
		{
			s_perfQueryReadyEvent.Reset();
			if (s_FifoShuttingDown.IsSet())
				return 0;
			s_perfQueryRequested.Set();
			s_perfQueryReadyEvent.Wait();
		}
		else
			g_perf_query->FlushResults();
	}

	return g_perf_query->GetQueryResult(type);
}
Esempio n. 10
0
void HostDispatchJobs()
{
  // WARNING: This should only run on the Host Thread.
  // NOTE: This function is potentially re-entrant. If a job calls
  //   Core::Stop for instance then we'll enter this a second time.
  std::unique_lock<std::mutex> guard(s_host_jobs_lock);
  while (!s_host_jobs_queue.empty())
  {
    HostJob job = std::move(s_host_jobs_queue.front());
    s_host_jobs_queue.pop();

    // NOTE: Memory ordering is important. The booting flag needs to be
    //   checked first because the state transition is:
    //   CORE_UNINITIALIZED: s_is_booting -> s_hardware_initialized
    //   We need to check variables in the same order as the state
    //   transition, otherwise we race and get transient failures.
    if (!job.run_after_stop && !s_is_booting.IsSet() && !IsRunning())
      continue;

    guard.unlock();
    job.job();
    guard.lock();
  }
}
Esempio n. 11
0
void HiresTexture::Prefetch()
{
  Common::SetCurrentThreadName("Prefetcher");
  const size_t total = s_textureMap.size();
  size_t count = 0;
  size_t notification = 10;
  u32 starttime = Common::Timer::GetTimeMs();
  for (const auto& entry : s_textureMap)
  {
    const std::string& base_filename = entry.first;

    std::unique_lock<std::mutex> lk(s_textureCacheMutex);

    auto iter = s_textureCache.find(base_filename);
    if (iter == s_textureCache.end())
    {
      lk.unlock();
      HiresTexture* ptr =
          Load(base_filename, [](size_t requested_size) { return new u8[requested_size]; }, true);
      lk.lock();
      if (ptr != nullptr)
      {
        size_sum.fetch_add(ptr->m_cached_data_size);
        iter = s_textureCache.insert(
            iter, std::make_pair(base_filename, std::shared_ptr<HiresTexture>(ptr)));
      }
    }

    if (s_textureCacheAbortLoading.IsSet())
    {
      if (g_ActiveConfig.bWaitForCacheHiresTextures)
      {
        Host_UpdateProgressDialog("", -1, -1);
      }
      return;
    }

    if (size_sum.load() > max_mem)
    {
      Config::SetCurrent(Config::GFX_HIRES_TEXTURES, false);

      OSD::AddMessage(
          StringFromFormat(
              "Custom Textures prefetching after %.1f MB aborted, not enough RAM available",
              size_sum / (1024.0 * 1024.0)),
          10000);
      if (g_ActiveConfig.bWaitForCacheHiresTextures)
      {
        Host_UpdateProgressDialog("", -1, -1);
      }
      return;
    }
    count++;
    size_t percent = (count * 100) / total;
    if (percent >= notification)
    {
      if (g_ActiveConfig.bWaitForCacheHiresTextures)
      {
        Host_UpdateProgressDialog(GetStringT("Prefetching Custom Textures...").c_str(),
                                  static_cast<int>(count), static_cast<int>(total));
      }
      else
      {
        OSD::AddMessage(StringFromFormat("Custom Textures prefetching %.1f MB %zu %% finished",
                                         size_sum / (1024.0 * 1024.0), percent),
                        2000);
      }
      notification += 10;
    }
  }
  if (g_ActiveConfig.bWaitForCacheHiresTextures)
  {
    Host_UpdateProgressDialog("", -1, -1);
  }
  u32 stoptime = Common::Timer::GetTimeMs();
  OSD::AddMessage(StringFromFormat("Custom Textures loaded, %.1f MB in %.1f s",
                                   size_sum / (1024.0 * 1024.0), (stoptime - starttime) / 1000.0),
                  10000);
}
Esempio n. 12
0
int main(int argc, char* argv[])
{
  auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
  optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
  std::vector<std::string> args = parser->args();

  std::string boot_filename;
  if (options.is_set("exec"))
  {
    boot_filename = static_cast<const char*>(options.get("exec"));
  }
  else if (args.size())
  {
    boot_filename = args.front();
    args.erase(args.begin());
  }
  else
  {
    parser->print_help();
    return 0;
  }

  std::string user_directory;
  if (options.is_set("user"))
  {
    user_directory = static_cast<const char*>(options.get("user"));
  }

  platform = GetPlatform();
  if (!platform)
  {
    fprintf(stderr, "No platform found\n");
    return 1;
  }

  UICommon::SetUserDirectory(user_directory);
  UICommon::Init();

  Core::SetOnStoppedCallback([]() { s_running.Clear(); });
  platform->Init();

  // Shut down cleanly on SIGINT and SIGTERM
  struct sigaction sa;
  sa.sa_handler = signal_handler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESETHAND;
  sigaction(SIGINT, &sa, nullptr);
  sigaction(SIGTERM, &sa, nullptr);

  DolphinAnalytics::Instance()->ReportDolphinStart("nogui");

  if (!BootManager::BootCore(BootParameters::GenerateFromFile(boot_filename)))
  {
    fprintf(stderr, "Could not boot %s\n", boot_filename.c_str());
    return 1;
  }

  while (!Core::IsRunning() && s_running.IsSet())
  {
    Core::HostDispatchJobs();
    updateMainFrameEvent.Wait();
  }

  if (s_running.IsSet())
    platform->MainLoop();
  Core::Stop();

  Core::Shutdown();
  platform->Shutdown();
  UICommon::Shutdown();

  delete platform;

  return 0;
}
Esempio n. 13
0
  void MainLoop() override
  {
    bool fullscreen = SConfig::GetInstance().bFullscreen;
    int last_window_width = SConfig::GetInstance().iRenderWindowWidth;
    int last_window_height = SConfig::GetInstance().iRenderWindowHeight;

    if (fullscreen)
    {
      rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win);
#if defined(HAVE_XRANDR) && HAVE_XRANDR
      XRRConfig->ToggleDisplayMode(True);
#endif
    }

    // The actual loop
    while (s_running.IsSet())
    {
      if (s_shutdown_requested.TestAndClear())
      {
        const auto ios = IOS::HLE::GetIOS();
        const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr;
        if (!s_tried_graceful_shutdown.IsSet() && stm &&
            std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
        {
          ProcessorInterface::PowerButton_Tap();
          s_tried_graceful_shutdown.Set();
        }
        else
        {
          s_running.Clear();
        }
      }

      XEvent event;
      KeySym key;
      for (int num_events = XPending(dpy); num_events > 0; num_events--)
      {
        XNextEvent(dpy, &event);
        switch (event.type)
        {
        case KeyPress:
          key = XLookupKeysym((XKeyEvent*)&event, 0);
          if (key == XK_Escape)
          {
            if (Core::GetState() == Core::State::Running)
            {
              if (SConfig::GetInstance().bHideCursor)
                XUndefineCursor(dpy, win);
              Core::SetState(Core::State::Paused);
            }
            else
            {
              if (SConfig::GetInstance().bHideCursor)
                XDefineCursor(dpy, win, blankCursor);
              Core::SetState(Core::State::Running);
            }
          }
          else if ((key == XK_Return) && (event.xkey.state & Mod1Mask))
          {
            fullscreen = !fullscreen;
            X11Utils::ToggleFullscreen(dpy, win);
#if defined(HAVE_XRANDR) && HAVE_XRANDR
            XRRConfig->ToggleDisplayMode(fullscreen);
#endif
          }
          else if (key >= XK_F1 && key <= XK_F8)
          {
            int slot_number = key - XK_F1 + 1;
            if (event.xkey.state & ShiftMask)
              State::Save(slot_number);
            else
              State::Load(slot_number);
          }
          else if (key == XK_F9)
            Core::SaveScreenShot();
          else if (key == XK_F11)
            State::LoadLastSaved();
          else if (key == XK_F12)
          {
            if (event.xkey.state & ShiftMask)
              State::UndoLoadState();
            else
              State::UndoSaveState();
          }
          break;
        case FocusIn:
          rendererHasFocus = true;
          if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused)
            XDefineCursor(dpy, win, blankCursor);
          break;
        case FocusOut:
          rendererHasFocus = false;
          if (SConfig::GetInstance().bHideCursor)
            XUndefineCursor(dpy, win);
          break;
        case ClientMessage:
          if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False))
            s_shutdown_requested.Set();
          break;
        case ConfigureNotify:
        {
          if (last_window_width != event.xconfigure.width ||
              last_window_height != event.xconfigure.height)
          {
            last_window_width = event.xconfigure.width;
            last_window_height = event.xconfigure.height;

            // We call Renderer::ChangeSurface here to indicate the size has changed,
            // but pass the same window handle. This is needed for the Vulkan backend,
            // otherwise it cannot tell that the window has been resized on some drivers.
            if (g_renderer)
              g_renderer->ChangeSurface(s_window_handle);
          }
        }
        break;
        }
      }
      if (!fullscreen)
      {
        Window winDummy;
        unsigned int borderDummy, depthDummy;
        XGetGeometry(dpy, win, &winDummy, &SConfig::GetInstance().iRenderWindowXPos,
                     &SConfig::GetInstance().iRenderWindowYPos,
                     (unsigned int*)&SConfig::GetInstance().iRenderWindowWidth,
                     (unsigned int*)&SConfig::GetInstance().iRenderWindowHeight, &borderDummy,
                     &depthDummy);
        rendererIsFullscreen = false;
      }
      Core::HostDispatchJobs();
      usleep(100000);
    }
  }
Esempio n. 14
0
// Description: Main FIFO update loop
// Purpose: Keep the Core HW updated about the CPU-GPU distance
void RunGpuLoop()
{
  AsyncRequests::GetInstance()->SetEnable(true);
  AsyncRequests::GetInstance()->SetPassthrough(false);

  s_gpu_mainloop.Run(
      [] {
        const SConfig& param = SConfig::GetInstance();

        g_video_backend->PeekMessages();

        // Do nothing while paused
        if (!s_emu_running_state.IsSet())
          return;

        if (s_use_deterministic_gpu_thread)
        {
          AsyncRequests::GetInstance()->PullEvents();

          // All the fifo/CP stuff is on the CPU.  We just need to run the opcode decoder.
          u8* seen_ptr = s_video_buffer_seen_ptr;
          u8* write_ptr = s_video_buffer_write_ptr;
          // See comment in SyncGPU
          if (write_ptr > seen_ptr)
          {
            s_video_buffer_read_ptr =
                OpcodeDecoder::Run(DataReader(s_video_buffer_read_ptr, write_ptr), nullptr, false);
            s_video_buffer_seen_ptr = write_ptr;
          }
        }
        else
        {
          SCPFifoStruct& fifo = CommandProcessor::fifo;

          AsyncRequests::GetInstance()->PullEvents();

          CommandProcessor::SetCPStatusFromGPU();

          // check if we are able to run this buffer
          while (!CommandProcessor::IsInterruptWaiting() && fifo.bFF_GPReadEnable &&
                 fifo.CPReadWriteDistance && !AtBreakpoint())
          {
            if (param.bSyncGPU && s_sync_ticks.load() < param.iSyncGpuMinDistance)
              break;

            u32 cyclesExecuted = 0;
            u32 readPtr = fifo.CPReadPointer;
            ReadDataFromFifo(readPtr);

            if (readPtr == fifo.CPEnd)
              readPtr = fifo.CPBase;
            else
              readPtr += 32;

            _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0,
                         "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce "
                         "instability in the game. Please report it.",
                         fifo.CPReadWriteDistance - 32);

            u8* write_ptr = s_video_buffer_write_ptr;
            s_video_buffer_read_ptr = OpcodeDecoder::Run(
                DataReader(s_video_buffer_read_ptr, write_ptr), &cyclesExecuted, false);

            Common::AtomicStore(fifo.CPReadPointer, readPtr);
            Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
            if ((write_ptr - s_video_buffer_read_ptr) == 0)
              Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);

            CommandProcessor::SetCPStatusFromGPU();

            if (param.bSyncGPU)
            {
              cyclesExecuted = (int)(cyclesExecuted / param.fSyncGpuOverclock);
              int old = s_sync_ticks.fetch_sub(cyclesExecuted);
              if (old >= param.iSyncGpuMaxDistance &&
                  old - (int)cyclesExecuted < param.iSyncGpuMaxDistance)
                s_sync_wakeup_event.Set();
            }

            // This call is pretty important in DualCore mode and must be called in the FIFO Loop.
            // If we don't, s_swapRequested or s_efbAccessRequested won't be set to false
            // leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing
            // things down.
            AsyncRequests::GetInstance()->PullEvents();
          }

          // fast skip remaining GPU time if fifo is empty
          if (s_sync_ticks.load() > 0)
          {
            int old = s_sync_ticks.exchange(0);
            if (old >= param.iSyncGpuMaxDistance)
              s_sync_wakeup_event.Set();
          }

          // The fifo is empty and it's unlikely we will get any more work in the near future.
          // Make sure VertexManager finishes drawing any primitives it has stored in it's buffer.
          g_vertex_manager->Flush();
        }
      },
      100);

  AsyncRequests::GetInstance()->SetEnable(false);
  AsyncRequests::GetInstance()->SetPassthrough(true);
}
Esempio n. 15
0
void HiresTexture::Prefetch()
{
	Common::SetCurrentThreadName("Prefetcher");

	size_t size_sum = 0;
	size_t sys_mem = MemPhysical();
	size_t recommended_min_mem = 2 * size_t(1024 * 1024 * 1024);
	// keep 2GB memory for system stability if system RAM is 4GB+ - use half of memory in other cases
	size_t max_mem = (sys_mem / 2 < recommended_min_mem) ? (sys_mem / 2) : (sys_mem - recommended_min_mem);
	u32 starttime = Common::Timer::GetTimeMs();
	for (const auto& entry : s_textureMap)
	{
		const std::string& base_filename = entry.first;

		if (base_filename.find("_mip") == std::string::npos)
		{
			{
				// try to get this mutex first, so the video thread is allow to get the real mutex faster
				std::unique_lock<std::mutex> lk(s_textureCacheAquireMutex);
			}
			std::unique_lock<std::mutex> lk(s_textureCacheMutex);

			auto iter = s_textureCache.find(base_filename);
			if (iter == s_textureCache.end())
			{
				// unlock while loading a texture. This may result in a race condition where we'll load a texture twice,
				// but it reduces the stuttering a lot. Notice: The loading library _must_ be thread safe now.
				// But bad luck, SOIL isn't, so TODO: remove SOIL usage here and use libpng directly
				// Also TODO: remove s_textureCacheAquireMutex afterwards. It won't be needed as the main mutex will be locked rarely
				//lk.unlock();
				std::unique_ptr<HiresTexture> texture = Load(base_filename, 0, 0);
				//lk.lock();
				if (texture)
				{
					std::shared_ptr<HiresTexture> ptr(std::move(texture));
					iter = s_textureCache.insert(iter, std::make_pair(base_filename, ptr));
				}
			}
			if (iter != s_textureCache.end())
			{
				for (const Level& l : iter->second->m_levels)
				{
					size_sum += l.data_size;
				}
			}
		}

		if (s_textureCacheAbortLoading.IsSet())
		{
			return;
		}

		if (size_sum > max_mem)
		{
			g_Config.bCacheHiresTextures = false;

			OSD::AddMessage(StringFromFormat("Custom Textures prefetching after %.1f MB aborted, not enough RAM available", size_sum / (1024.0 * 1024.0)), 10000);
			return;
		}
	}
	u32 stoptime = Common::Timer::GetTimeMs();
	OSD::AddMessage(StringFromFormat("Custom Textures loaded, %.1f MB in %.1f s", size_sum / (1024.0 * 1024.0), (stoptime - starttime) / 1000.0), 10000);
}