mirror of https://github.com/PCSX2/pcsx2.git
CDVD: Add precaching option
This commit is contained in:
parent
e1596c7911
commit
7ad27e6e9d
|
@ -101,40 +101,48 @@ void ProgressCallback::DisplayFormattedModalInformation(const char* format, ...)
|
||||||
ModalInformation(str.c_str());
|
ModalInformation(str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullProgressCallbacks final : public ProgressCallback
|
namespace
|
||||||
{
|
{
|
||||||
public:
|
class NullProgressCallbacks final : public ProgressCallback
|
||||||
void PushState() override {}
|
|
||||||
void PopState() override {}
|
|
||||||
|
|
||||||
bool IsCancelled() const override { return false; }
|
|
||||||
bool IsCancellable() const override { return false; }
|
|
||||||
|
|
||||||
void SetCancellable(bool cancellable) override {}
|
|
||||||
void SetTitle(const char* title) override {}
|
|
||||||
void SetStatusText(const char* statusText) override {}
|
|
||||||
void SetProgressRange(u32 range) override {}
|
|
||||||
void SetProgressValue(u32 value) override {}
|
|
||||||
void IncrementProgressValue() override {}
|
|
||||||
void SetProgressState(ProgressState state) override {}
|
|
||||||
|
|
||||||
void DisplayError(const char* message) override { Console.Error("%s", message); }
|
|
||||||
void DisplayWarning(const char* message) override { Console.Warning("%s", message); }
|
|
||||||
void DisplayInformation(const char* message) override { Console.WriteLn("%s", message); }
|
|
||||||
void DisplayDebugMessage(const char* message) override { DevCon.WriteLn("%s", message); }
|
|
||||||
|
|
||||||
void ModalError(const char* message) override { Console.Error(message); }
|
|
||||||
bool ModalConfirmation(const char* message) override
|
|
||||||
{
|
{
|
||||||
Console.WriteLn("%s", message);
|
public:
|
||||||
return false;
|
void PushState() override {}
|
||||||
}
|
void PopState() override {}
|
||||||
void ModalInformation(const char* message) override { Console.WriteLn("%s", message); }
|
|
||||||
};
|
bool IsCancelled() const override { return false; }
|
||||||
|
bool IsCancellable() const override { return false; }
|
||||||
|
|
||||||
|
void SetCancellable(bool cancellable) override {}
|
||||||
|
void SetTitle(const char* title) override {}
|
||||||
|
void SetStatusText(const char* statusText) override {}
|
||||||
|
void SetProgressRange(u32 range) override {}
|
||||||
|
void SetProgressValue(u32 value) override {}
|
||||||
|
void IncrementProgressValue() override {}
|
||||||
|
void SetProgressState(ProgressState state) override {}
|
||||||
|
|
||||||
|
void DisplayError(const char* message) override { Console.Error("%s", message); }
|
||||||
|
void DisplayWarning(const char* message) override { Console.Warning("%s", message); }
|
||||||
|
void DisplayInformation(const char* message) override { Console.WriteLn("%s", message); }
|
||||||
|
void DisplayDebugMessage(const char* message) override { DevCon.WriteLn("%s", message); }
|
||||||
|
|
||||||
|
void ModalError(const char* message) override { Console.Error(message); }
|
||||||
|
bool ModalConfirmation(const char* message) override
|
||||||
|
{
|
||||||
|
Console.WriteLn("%s", message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ModalInformation(const char* message) override { Console.WriteLn("%s", message); }
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static NullProgressCallbacks s_nullProgressCallbacks;
|
static NullProgressCallbacks s_nullProgressCallbacks;
|
||||||
ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks;
|
ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks;
|
||||||
|
|
||||||
|
std::unique_ptr<ProgressCallback> ProgressCallback::CreateNullProgressCallback()
|
||||||
|
{
|
||||||
|
return std::make_unique<NullProgressCallbacks>();
|
||||||
|
}
|
||||||
|
|
||||||
BaseProgressCallback::BaseProgressCallback()
|
BaseProgressCallback::BaseProgressCallback()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -171,8 +179,8 @@ void BaseProgressCallback::PopState()
|
||||||
// impose the current position into the previous range
|
// impose the current position into the previous range
|
||||||
const u32 new_progress_value =
|
const u32 new_progress_value =
|
||||||
(m_progress_range != 0) ?
|
(m_progress_range != 0) ?
|
||||||
static_cast<u32>(((float)m_progress_value / (float)m_progress_range) * (float)state->progress_range) :
|
static_cast<u32>(((float)m_progress_value / (float)m_progress_range) * (float)state->progress_range) :
|
||||||
state->progress_value;
|
state->progress_value;
|
||||||
|
|
||||||
m_cancellable = state->cancellable;
|
m_cancellable = state->cancellable;
|
||||||
m_status_text = std::move(state->status_text);
|
m_status_text = std::move(state->status_text);
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Pcsx2Defs.h"
|
#include "Pcsx2Defs.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +61,8 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static ProgressCallback* NullProgressCallback;
|
static ProgressCallback* NullProgressCallback;
|
||||||
|
|
||||||
|
static std::unique_ptr<ProgressCallback> CreateNullProgressCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseProgressCallback : public ProgressCallback
|
class BaseProgressCallback : public ProgressCallback
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/MemorySettingsInterface.h"
|
#include "common/MemorySettingsInterface.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
#include "common/ProgressCallback.h"
|
||||||
#include "common/SettingsWrapper.h"
|
#include "common/SettingsWrapper.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
|
@ -165,6 +166,11 @@ void Host::SetDefaultUISettings(SettingsInterface& si)
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ProgressCallback> Host::CreateHostProgressCallback()
|
||||||
|
{
|
||||||
|
return ProgressCallback::CreateNullProgressCallback();
|
||||||
|
}
|
||||||
|
|
||||||
void Host::ReportErrorAsync(const std::string_view title, const std::string_view message)
|
void Host::ReportErrorAsync(const std::string_view title, const std::string_view message)
|
||||||
{
|
{
|
||||||
if (!title.empty() && !message.empty())
|
if (!title.empty() && !message.empty())
|
||||||
|
|
|
@ -1704,6 +1704,247 @@ void Host::SetMouseMode(bool relative_mode, bool hide_cursor)
|
||||||
emit g_emu_thread->onMouseModeRequested(relative_mode, hide_cursor);
|
emit g_emu_thread->onMouseModeRequested(relative_mode, hide_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class QtHostProgressCallback final : public BaseProgressCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QtHostProgressCallback();
|
||||||
|
~QtHostProgressCallback() override;
|
||||||
|
|
||||||
|
__fi const std::string& GetName() const { return m_name; }
|
||||||
|
|
||||||
|
void PushState() override;
|
||||||
|
void PopState() override;
|
||||||
|
|
||||||
|
bool IsCancelled() const override;
|
||||||
|
|
||||||
|
void SetCancellable(bool cancellable) override;
|
||||||
|
void SetTitle(const char* title) override;
|
||||||
|
void SetStatusText(const char* text) override;
|
||||||
|
void SetProgressRange(u32 range) override;
|
||||||
|
void SetProgressValue(u32 value) override;
|
||||||
|
|
||||||
|
void DisplayError(const char* message) override;
|
||||||
|
void DisplayWarning(const char* message) override;
|
||||||
|
void DisplayInformation(const char* message) override;
|
||||||
|
void DisplayDebugMessage(const char* message) override;
|
||||||
|
|
||||||
|
void ModalError(const char* message) override;
|
||||||
|
bool ModalConfirmation(const char* message) override;
|
||||||
|
void ModalInformation(const char* message) override;
|
||||||
|
|
||||||
|
void SetCancelled();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct SharedData
|
||||||
|
{
|
||||||
|
QProgressDialog* dialog = nullptr;
|
||||||
|
QString init_title;
|
||||||
|
QString init_status_text;
|
||||||
|
std::atomic_bool cancelled{false};
|
||||||
|
bool cancellable = true;
|
||||||
|
bool was_fullscreen = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void EnsureHasData();
|
||||||
|
static void EnsureDialogVisible(const std::shared_ptr<SharedData>& data);
|
||||||
|
void Redraw(bool force);
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::shared_ptr<SharedData> m_data;
|
||||||
|
int m_last_progress_percent = -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QtHostProgressCallback::QtHostProgressCallback()
|
||||||
|
: BaseProgressCallback()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QtHostProgressCallback::~QtHostProgressCallback()
|
||||||
|
{
|
||||||
|
if (m_data)
|
||||||
|
{
|
||||||
|
QtHost::RunOnUIThread([data = m_data]() {
|
||||||
|
if (!data->dialog)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data->dialog->close();
|
||||||
|
delete data->dialog;
|
||||||
|
if (data->was_fullscreen)
|
||||||
|
g_emu_thread->setFullscreen(true, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::PushState()
|
||||||
|
{
|
||||||
|
BaseProgressCallback::PushState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::PopState()
|
||||||
|
{
|
||||||
|
BaseProgressCallback::PopState();
|
||||||
|
Redraw(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetCancellable(bool cancellable)
|
||||||
|
{
|
||||||
|
BaseProgressCallback::SetCancellable(cancellable);
|
||||||
|
EnsureHasData();
|
||||||
|
m_data->cancellable = cancellable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetTitle(const char* title)
|
||||||
|
{
|
||||||
|
EnsureHasData();
|
||||||
|
QtHost::RunOnUIThread([data = m_data, title = QString::fromUtf8(title)]() {
|
||||||
|
if (data->dialog)
|
||||||
|
data->dialog->setWindowTitle(title);
|
||||||
|
else
|
||||||
|
data->init_title = title;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetStatusText(const char* text)
|
||||||
|
{
|
||||||
|
BaseProgressCallback::SetStatusText(text);
|
||||||
|
|
||||||
|
EnsureHasData();
|
||||||
|
QtHost::RunOnUIThread([data = m_data, text = QString::fromUtf8(text)]() {
|
||||||
|
if (data->dialog)
|
||||||
|
data->dialog->setLabelText(text);
|
||||||
|
else
|
||||||
|
data->init_status_text = text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetProgressRange(u32 range)
|
||||||
|
{
|
||||||
|
u32 last_range = m_progress_range;
|
||||||
|
|
||||||
|
BaseProgressCallback::SetProgressRange(range);
|
||||||
|
|
||||||
|
if (m_progress_range != last_range)
|
||||||
|
Redraw(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetProgressValue(u32 value)
|
||||||
|
{
|
||||||
|
u32 lastValue = m_progress_value;
|
||||||
|
|
||||||
|
BaseProgressCallback::SetProgressValue(value);
|
||||||
|
|
||||||
|
if (m_progress_value != lastValue)
|
||||||
|
Redraw(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::Redraw(bool force)
|
||||||
|
{
|
||||||
|
const int percent = static_cast<int>((static_cast<float>(m_progress_value) / static_cast<float>(m_progress_range)) * 100.0f);
|
||||||
|
if (percent == m_last_progress_percent && !force)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If this is the emu uthread, we need to process the un-fullscreen message.
|
||||||
|
if (g_emu_thread->isOnEmuThread())
|
||||||
|
Host::PumpMessagesOnCPUThread();
|
||||||
|
|
||||||
|
m_last_progress_percent = percent;
|
||||||
|
EnsureHasData();
|
||||||
|
QtHost::RunOnUIThread([data = m_data, percent]() {
|
||||||
|
EnsureDialogVisible(data);
|
||||||
|
data->dialog->setValue(percent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::DisplayError(const char* message)
|
||||||
|
{
|
||||||
|
Console.Error(message);
|
||||||
|
Host::ReportErrorAsync("Error", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::DisplayWarning(const char* message)
|
||||||
|
{
|
||||||
|
Console.Warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::DisplayInformation(const char* message)
|
||||||
|
{
|
||||||
|
Console.WriteLn(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::DisplayDebugMessage(const char* message)
|
||||||
|
{
|
||||||
|
DevCon.WriteLn(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::ModalError(const char* message)
|
||||||
|
{
|
||||||
|
Console.Error(message);
|
||||||
|
Host::ReportErrorAsync("Error", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtHostProgressCallback::ModalConfirmation(const char* message)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::ModalInformation(const char* message)
|
||||||
|
{
|
||||||
|
Console.WriteLn(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::SetCancelled()
|
||||||
|
{
|
||||||
|
// not done here
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtHostProgressCallback::IsCancelled() const
|
||||||
|
{
|
||||||
|
return m_data && m_data->cancelled.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::EnsureHasData()
|
||||||
|
{
|
||||||
|
if (!m_data)
|
||||||
|
m_data = std::make_shared<SharedData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostProgressCallback::EnsureDialogVisible(const std::shared_ptr<SharedData>& data)
|
||||||
|
{
|
||||||
|
pxAssert(data);
|
||||||
|
if (data->dialog)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data->was_fullscreen = g_emu_thread->isFullscreen();
|
||||||
|
if (data->was_fullscreen)
|
||||||
|
g_emu_thread->setFullscreen(false, true);
|
||||||
|
|
||||||
|
data->dialog = new QProgressDialog(data->init_status_text,
|
||||||
|
data->cancellable ? qApp->translate("QtHost", "Cancel") : QString(),
|
||||||
|
0, 100, g_main_window);
|
||||||
|
if (data->cancellable)
|
||||||
|
{
|
||||||
|
data->dialog->connect(data->dialog, &QProgressDialog::canceled,
|
||||||
|
[data]() { data->cancelled.store(true, std::memory_order_release); });
|
||||||
|
}
|
||||||
|
data->dialog->setWindowIcon(QtHost::GetAppIcon());
|
||||||
|
data->dialog->setMinimumWidth(400);
|
||||||
|
data->dialog->show();
|
||||||
|
data->dialog->raise();
|
||||||
|
data->dialog->activateWindow();
|
||||||
|
if (!data->init_title.isEmpty())
|
||||||
|
{
|
||||||
|
data->dialog->setWindowTitle(data->init_title);
|
||||||
|
data->init_title = QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ProgressCallback> Host::CreateHostProgressCallback()
|
||||||
|
{
|
||||||
|
return std::make_unique<QtHostProgressCallback>();
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Hotkeys
|
// Hotkeys
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -114,7 +114,7 @@ public Q_SLOTS:
|
||||||
void endCapture();
|
void endCapture();
|
||||||
void setAudioOutputVolume(int volume, int fast_forward_volume);
|
void setAudioOutputVolume(int volume, int fast_forward_volume);
|
||||||
void setAudioOutputMuted(bool muted);
|
void setAudioOutputMuted(bool muted);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
bool messageConfirmed(const QString& title, const QString& message);
|
bool messageConfirmed(const QString& title, const QString& message);
|
||||||
void statusMessage(const QString& message);
|
void statusMessage(const QString& message);
|
||||||
|
|
|
@ -45,6 +45,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheCDVD, "EmuCore", "CdvdPrecache", false);
|
||||||
|
|
||||||
if (m_dialog->isPerGameSettings())
|
if (m_dialog->isPerGameSettings())
|
||||||
{
|
{
|
||||||
|
@ -124,6 +125,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
"Safe for most games, but a few games may exhibit graphical errors."));
|
"Safe for most games, but a few games may exhibit graphical errors."));
|
||||||
dialog->registerWidgetHelp(m_ui.fastCDVD, tr("Enable Fast CDVD"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.fastCDVD, tr("Enable Fast CDVD"), tr("Unchecked"),
|
||||||
tr("Fast disc access, less loading times. Check HDLoader compatibility lists for known games that have issues with this."));
|
tr("Fast disc access, less loading times. Check HDLoader compatibility lists for known games that have issues with this."));
|
||||||
|
dialog->registerWidgetHelp(m_ui.precacheCDVD, tr("Enable CDVD Precaching"), tr("Unchecked"),
|
||||||
|
tr("Loads the disc image into RAM before starting the virtual machine. Can reduce stutter on systems with hard drives that "
|
||||||
|
"have long wake times, but significantly increases boot times."));
|
||||||
dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"),
|
||||||
tr("Automatically loads and applies cheats on game start."));
|
tr("Automatically loads and applies cheats on game start."));
|
||||||
dialog->registerWidgetHelp(m_ui.hostFilesystem, tr("Enable Host Filesystem"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.hostFilesystem, tr("Enable Host Filesystem"), tr("Unchecked"),
|
||||||
|
|
|
@ -98,13 +98,20 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="fastCDVD">
|
<widget class="QCheckBox" name="fastCDVD">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Fast CDVD</string>
|
<string>Enable Fast CDVD</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="precacheCDVD">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable CDVD Precaching</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include "common/EnumOps.h"
|
#include "common/EnumOps.h"
|
||||||
|
#include "common/Error.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
#include "common/ProgressCallback.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -413,6 +415,13 @@ bool DoCDVDopen(Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DoCDVDprecache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
CheckNullCDVD();
|
||||||
|
progress->SetTitle(TRANSLATE("CDVD", "Precaching CDVD"));
|
||||||
|
return CDVD->precache(progress, error);
|
||||||
|
}
|
||||||
|
|
||||||
void DoCDVDclose()
|
void DoCDVDclose()
|
||||||
{
|
{
|
||||||
CheckNullCDVD();
|
CheckNullCDVD();
|
||||||
|
@ -525,6 +534,11 @@ static bool NODISCopen(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool NODISCprecache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void NODISCclose()
|
static void NODISCclose()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -592,6 +606,7 @@ const CDVD_API CDVDapi_NoDisc =
|
||||||
{
|
{
|
||||||
NODISCclose,
|
NODISCclose,
|
||||||
NODISCopen,
|
NODISCopen,
|
||||||
|
NODISCprecache,
|
||||||
NODISCreadTrack,
|
NODISCreadTrack,
|
||||||
NODISCgetBuffer,
|
NODISCgetBuffer,
|
||||||
NODISCreadSubQ,
|
NODISCreadSubQ,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
|
class ProgressCallback;
|
||||||
|
|
||||||
typedef struct _cdvdSubQ
|
typedef struct _cdvdSubQ
|
||||||
{
|
{
|
||||||
|
@ -79,6 +80,7 @@ typedef struct _cdvdTN
|
||||||
|
|
||||||
// CDVD
|
// CDVD
|
||||||
typedef bool (*_CDVDopen)(std::string filename, Error* error);
|
typedef bool (*_CDVDopen)(std::string filename, Error* error);
|
||||||
|
typedef bool (*_CDVDprecache)(ProgressCallback* progress, Error* error);
|
||||||
|
|
||||||
// Initiates an asynchronous track read operation.
|
// Initiates an asynchronous track read operation.
|
||||||
// Returns -1 on error (invalid track)
|
// Returns -1 on error (invalid track)
|
||||||
|
@ -118,6 +120,7 @@ struct CDVD_API
|
||||||
// Don't need init or shutdown. iso/nodisc have no init/shutdown.
|
// Don't need init or shutdown. iso/nodisc have no init/shutdown.
|
||||||
|
|
||||||
_CDVDopen open;
|
_CDVDopen open;
|
||||||
|
_CDVDprecache precache;
|
||||||
_CDVDreadTrack readTrack;
|
_CDVDreadTrack readTrack;
|
||||||
_CDVDgetBuffer getBuffer;
|
_CDVDgetBuffer getBuffer;
|
||||||
_CDVDreadSubQ readSubQ;
|
_CDVDreadSubQ readSubQ;
|
||||||
|
@ -152,6 +155,7 @@ extern CDVD_SourceType CDVDsys_GetSourceType();
|
||||||
extern void CDVDsys_ClearFiles();
|
extern void CDVDsys_ClearFiles();
|
||||||
|
|
||||||
extern bool DoCDVDopen(Error* error);
|
extern bool DoCDVDopen(Error* error);
|
||||||
|
extern bool DoCDVDprecache(ProgressCallback* progress, Error* error);
|
||||||
extern void DoCDVDclose();
|
extern void DoCDVDclose();
|
||||||
extern s32 DoCDVDreadSector(u8* buffer, u32 lsn, int mode);
|
extern s32 DoCDVDreadSector(u8* buffer, u32 lsn, int mode);
|
||||||
extern s32 DoCDVDreadTrack(u32 lsn, int mode);
|
extern s32 DoCDVDreadTrack(u32 lsn, int mode);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#include "CDVDdiscReader.h"
|
#include "CDVDdiscReader.h"
|
||||||
#include "CDVD/CDVD.h"
|
#include "CDVD/CDVD.h"
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
#include "common/Error.h"
|
#include "common/Error.h"
|
||||||
|
|
||||||
|
@ -192,6 +193,12 @@ static bool DISCopen(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool DISCprecache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, TRANSLATE_SV("CDVD", "Precaching is not supported for discs."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void DISCclose()
|
static void DISCclose()
|
||||||
{
|
{
|
||||||
StopKeepAliveThread();
|
StopKeepAliveThread();
|
||||||
|
@ -531,6 +538,7 @@ const CDVD_API CDVDapi_Disc =
|
||||||
{
|
{
|
||||||
DISCclose,
|
DISCclose,
|
||||||
DISCopen,
|
DISCopen,
|
||||||
|
DISCprecache,
|
||||||
DISCreadTrack,
|
DISCreadTrack,
|
||||||
DISCgetBuffer,
|
DISCgetBuffer,
|
||||||
DISCreadSubQ,
|
DISCreadSubQ,
|
||||||
|
|
|
@ -55,6 +55,11 @@ static bool ISOopen(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ISOprecache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
return iso.Precache(progress, error);
|
||||||
|
}
|
||||||
|
|
||||||
static s32 ISOreadSubQ(u32 lsn, cdvdSubQ* subq)
|
static s32 ISOreadSubQ(u32 lsn, cdvdSubQ* subq)
|
||||||
{
|
{
|
||||||
// fake it
|
// fake it
|
||||||
|
@ -400,6 +405,7 @@ const CDVD_API CDVDapi_Iso =
|
||||||
ISOclose,
|
ISOclose,
|
||||||
|
|
||||||
ISOopen,
|
ISOopen,
|
||||||
|
ISOprecache,
|
||||||
ISOreadTrack,
|
ISOreadTrack,
|
||||||
ISOgetBuffer,
|
ISOgetBuffer,
|
||||||
ISOreadSubQ,
|
ISOreadSubQ,
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "common/Error.h"
|
#include "common/Error.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
#include "common/ProgressCallback.h"
|
||||||
|
#include "common/SmallString.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include "libchdr/chd.h"
|
#include "libchdr/chd.h"
|
||||||
|
@ -188,6 +190,32 @@ bool ChdFileReader::Open2(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChdFileReader::Precache2(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
if (!CheckAvailableMemoryForPrecaching(chd_get_compressed_size(ChdFile), error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
progress->SetProgressRange(100);
|
||||||
|
|
||||||
|
const auto callback = [](size_t pos, size_t total, void* param) -> bool {
|
||||||
|
ProgressCallback* progress = static_cast<ProgressCallback*>(param);
|
||||||
|
const u32 percent = static_cast<u32>((pos * 100) / total);
|
||||||
|
progress->SetProgressValue(std::min<u32>(percent, 100));
|
||||||
|
return !progress->IsCancelled();
|
||||||
|
};
|
||||||
|
|
||||||
|
const chd_error cerror = chd_precache_progress(ChdFile, callback, progress);
|
||||||
|
if (cerror != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
if (cerror != CHDERR_CANCELLED)
|
||||||
|
Error::SetStringView(error, "Failed to read part of the file.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset)
|
ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset)
|
||||||
{
|
{
|
||||||
Chunk chunk = {0};
|
Chunk chunk = {0};
|
||||||
|
|
|
@ -16,6 +16,8 @@ public:
|
||||||
~ChdFileReader() override;
|
~ChdFileReader() override;
|
||||||
|
|
||||||
bool Open2(std::string filename, Error* error) override;
|
bool Open2(std::string filename, Error* error) override;
|
||||||
|
|
||||||
|
bool Precache2(ProgressCallback* progress, Error* error) override;
|
||||||
|
|
||||||
Chunk ChunkForOffset(u64 offset) override;
|
Chunk ChunkForOffset(u64 offset) override;
|
||||||
int ReadChunk(void* dst, s64 blockID) override;
|
int ReadChunk(void* dst, s64 blockID) override;
|
||||||
|
|
|
@ -84,6 +84,31 @@ bool CsoFileReader::Open2(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CsoFileReader::Precache2(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
if (!m_src)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const s64 size = FileSystem::FSize64(m_src);
|
||||||
|
if (size < 0 || !CheckAvailableMemoryForPrecaching(static_cast<u64>(size), error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_file_cache_size = static_cast<size_t>(size);
|
||||||
|
m_file_cache = std::make_unique_for_overwrite<u8[]>(m_file_cache_size);
|
||||||
|
if (FileSystem::FSeek64(m_src, 0, SEEK_SET) != 0 ||
|
||||||
|
FileSystem::ReadFileWithProgress(
|
||||||
|
m_src, m_file_cache.get(), m_file_cache_size, progress, error) != m_file_cache_size)
|
||||||
|
{
|
||||||
|
m_file_cache.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_readBuffer.reset();
|
||||||
|
std::fclose(m_src);
|
||||||
|
m_src = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CsoFileReader::ReadFileHeader(Error* error)
|
bool CsoFileReader::ReadFileHeader(Error* error)
|
||||||
{
|
{
|
||||||
CsoHeader hdr;
|
CsoHeader hdr;
|
||||||
|
@ -141,11 +166,7 @@ bool CsoFileReader::InitializeBuffers(Error* error)
|
||||||
// initialize zlib if not a ZSO
|
// initialize zlib if not a ZSO
|
||||||
if (!m_uselz4)
|
if (!m_uselz4)
|
||||||
{
|
{
|
||||||
m_z_stream = std::make_unique<z_stream>();
|
if (inflateInit2(&m_z_stream, -15) != Z_OK)
|
||||||
m_z_stream->zalloc = Z_NULL;
|
|
||||||
m_z_stream->zfree = Z_NULL;
|
|
||||||
m_z_stream->opaque = Z_NULL;
|
|
||||||
if (inflateInit2(m_z_stream.get(), -15) != Z_OK)
|
|
||||||
{
|
{
|
||||||
Error::SetString(error, "Unable to initialize zlib for CSO decompression.");
|
Error::SetString(error, "Unable to initialize zlib for CSO decompression.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -164,11 +185,10 @@ void CsoFileReader::Close2()
|
||||||
fclose(m_src);
|
fclose(m_src);
|
||||||
m_src = nullptr;
|
m_src = nullptr;
|
||||||
}
|
}
|
||||||
if (m_z_stream)
|
if (m_file_cache)
|
||||||
{
|
m_file_cache.reset();
|
||||||
inflateEnd(m_z_stream.get());
|
if (!m_uselz4)
|
||||||
m_z_stream.reset();
|
inflateEnd(&m_z_stream);
|
||||||
}
|
|
||||||
|
|
||||||
m_readBuffer.reset();
|
m_readBuffer.reset();
|
||||||
m_index.reset();
|
m_index.reset();
|
||||||
|
@ -213,6 +233,16 @@ int CsoFileReader::ReadChunk(void* dst, s64 chunkID)
|
||||||
|
|
||||||
if (!compressed)
|
if (!compressed)
|
||||||
{
|
{
|
||||||
|
if (m_file_cache)
|
||||||
|
{
|
||||||
|
if (frameRawPos >= m_file_cache_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const size_t read_count = std::min<size_t>(m_file_cache_size - frameRawPos, frameRawSize);
|
||||||
|
std::memcpy(dst, &m_file_cache[frameRawPos], read_count);
|
||||||
|
return static_cast<int>(read_count);
|
||||||
|
}
|
||||||
|
|
||||||
// Just read directly, easy.
|
// Just read directly, easy.
|
||||||
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
||||||
{
|
{
|
||||||
|
@ -223,21 +253,36 @@ int CsoFileReader::ReadChunk(void* dst, s64 chunkID)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
|
||||||
{
|
|
||||||
Console.Error("Unable to seek to compressed CSO data.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// This might be less bytes than frameRawSize in case of padding on the last frame.
|
// This might be less bytes than frameRawSize in case of padding on the last frame.
|
||||||
// This is because the index positions must be aligned.
|
// This is because the index positions must be aligned.
|
||||||
const u32 readRawBytes = fread(m_readBuffer.get(), 1, frameRawSize, m_src);
|
u32 readRawBytes;
|
||||||
|
u8* readBuffer;
|
||||||
|
if (m_file_cache)
|
||||||
|
{
|
||||||
|
if (frameRawPos >= m_file_cache_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
readRawBytes = static_cast<u32>(std::min<size_t>(m_file_cache_size - frameRawPos, frameRawSize));
|
||||||
|
readBuffer = &m_file_cache[frameRawPos];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (FileSystem::FSeek64(m_src, frameRawPos, SEEK_SET) != 0)
|
||||||
|
{
|
||||||
|
Console.Error("Unable to seek to compressed CSO data.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
readBuffer = m_readBuffer.get();
|
||||||
|
readRawBytes = fread(m_readBuffer.get(), 1, frameRawSize, m_src);
|
||||||
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
if (m_uselz4)
|
if (m_uselz4)
|
||||||
{
|
{
|
||||||
const int src_size = static_cast<int>(readRawBytes);
|
const int src_size = static_cast<int>(readRawBytes);
|
||||||
const int dst_size = static_cast<int>(m_frameSize);
|
const int dst_size = static_cast<int>(m_frameSize);
|
||||||
const char* src_buf = reinterpret_cast<const char*>(m_readBuffer.get());
|
const char* src_buf = reinterpret_cast<const char*>(readBuffer);
|
||||||
char* dst_buf = static_cast<char*>(dst);
|
char* dst_buf = static_cast<char*>(dst);
|
||||||
|
|
||||||
const int res = LZ4_decompress_safe_partial(src_buf, dst_buf, src_size, dst_size, dst_size);
|
const int res = LZ4_decompress_safe_partial(src_buf, dst_buf, src_size, dst_size, dst_size);
|
||||||
|
@ -245,20 +290,20 @@ int CsoFileReader::ReadChunk(void* dst, s64 chunkID)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_z_stream->next_in = m_readBuffer.get();
|
m_z_stream.next_in = readBuffer;
|
||||||
m_z_stream->avail_in = readRawBytes;
|
m_z_stream.avail_in = readRawBytes;
|
||||||
m_z_stream->next_out = static_cast<Bytef*>(dst);
|
m_z_stream.next_out = static_cast<Bytef*>(dst);
|
||||||
m_z_stream->avail_out = m_frameSize;
|
m_z_stream.avail_out = m_frameSize;
|
||||||
|
|
||||||
const int status = inflate(m_z_stream.get(), Z_FINISH);
|
const int status = inflate(&m_z_stream, Z_FINISH);
|
||||||
success = (status == Z_STREAM_END && m_z_stream->total_out == m_frameSize);
|
success = (status == Z_STREAM_END && m_z_stream.total_out == m_frameSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
Console.Error(fmt::format("Unable to decompress CSO frame using {}", (m_uselz4)? "lz4":"zlib"));
|
Console.Error(fmt::format("Unable to decompress CSO frame using {}", (m_uselz4)? "lz4":"zlib"));
|
||||||
|
|
||||||
if (!m_uselz4)
|
if (!m_uselz4)
|
||||||
inflateReset(m_z_stream.get());
|
inflateReset(&m_z_stream);
|
||||||
|
|
||||||
return success ? m_frameSize : 0;
|
return success ? m_frameSize : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
struct CsoHeader;
|
struct CsoHeader;
|
||||||
typedef struct z_stream_s z_stream;
|
|
||||||
|
|
||||||
class CsoFileReader final : public ThreadedFileReader
|
class CsoFileReader final : public ThreadedFileReader
|
||||||
{
|
{
|
||||||
|
@ -19,6 +18,8 @@ public:
|
||||||
|
|
||||||
bool Open2(std::string filename, Error* error) override;
|
bool Open2(std::string filename, Error* error) override;
|
||||||
|
|
||||||
|
bool Precache2(ProgressCallback* progress, Error* error) override;
|
||||||
|
|
||||||
Chunk ChunkForOffset(u64 offset) override;
|
Chunk ChunkForOffset(u64 offset) override;
|
||||||
int ReadChunk(void* dst, s64 chunkID) override;
|
int ReadChunk(void* dst, s64 chunkID) override;
|
||||||
|
|
||||||
|
@ -44,5 +45,7 @@ private:
|
||||||
u64 m_totalSize = 0;
|
u64 m_totalSize = 0;
|
||||||
// The actual source cso file handle.
|
// The actual source cso file handle.
|
||||||
std::FILE* m_src = nullptr;
|
std::FILE* m_src = nullptr;
|
||||||
std::unique_ptr<z_stream> m_z_stream;
|
std::unique_ptr<u8[]> m_file_cache;
|
||||||
|
size_t m_file_cache_size = 0;
|
||||||
|
z_stream m_z_stream = {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,6 +38,25 @@ bool FlatFileReader::Open2(std::string filename, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlatFileReader::Precache2(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
if (!m_file || !CheckAvailableMemoryForPrecaching(m_file_size, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_file_cache = std::make_unique_for_overwrite<u8[]>(m_file_size);
|
||||||
|
if (FileSystem::FSeek64(m_file, 0, SEEK_SET) != 0 ||
|
||||||
|
FileSystem::ReadFileWithProgress(
|
||||||
|
m_file, m_file_cache.get(), m_file_size, progress, error) != m_file_size)
|
||||||
|
{
|
||||||
|
m_file_cache.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fclose(m_file);
|
||||||
|
m_file = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadedFileReader::Chunk FlatFileReader::ChunkForOffset(u64 offset)
|
ThreadedFileReader::Chunk FlatFileReader::ChunkForOffset(u64 offset)
|
||||||
{
|
{
|
||||||
ThreadedFileReader::Chunk chunk = {};
|
ThreadedFileReader::Chunk chunk = {};
|
||||||
|
@ -61,6 +80,16 @@ int FlatFileReader::ReadChunk(void* dst, s64 blockID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const u64 file_offset = static_cast<u64>(blockID) * CHUNK_SIZE;
|
const u64 file_offset = static_cast<u64>(blockID) * CHUNK_SIZE;
|
||||||
|
if (m_file_cache)
|
||||||
|
{
|
||||||
|
if (file_offset >= m_file_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const u64 read_size = std::min<u64>(m_file_size - file_offset, CHUNK_SIZE);
|
||||||
|
std::memcpy(dst, &m_file_cache[file_offset], read_size);
|
||||||
|
return static_cast<int>(read_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (FileSystem::FSeek64(m_file, file_offset, SEEK_SET) != 0)
|
if (FileSystem::FSeek64(m_file, file_offset, SEEK_SET) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ class FlatFileReader final : public ThreadedFileReader
|
||||||
DeclareNoncopyableObject(FlatFileReader);
|
DeclareNoncopyableObject(FlatFileReader);
|
||||||
|
|
||||||
std::FILE* m_file = nullptr;
|
std::FILE* m_file = nullptr;
|
||||||
|
std::unique_ptr<u8[]> m_file_cache;
|
||||||
u64 m_file_size = 0;
|
u64 m_file_size = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -20,6 +21,8 @@ public:
|
||||||
|
|
||||||
bool Open2(std::string filename, Error* error) override;
|
bool Open2(std::string filename, Error* error) override;
|
||||||
|
|
||||||
|
bool Precache2(ProgressCallback* progress, Error* error) override;
|
||||||
|
|
||||||
Chunk ChunkForOffset(u64 offset) override;
|
Chunk ChunkForOffset(u64 offset) override;
|
||||||
int ReadChunk(void* dst, s64 blockID) override;
|
int ReadChunk(void* dst, s64 blockID) override;
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,11 @@ bool InputIsoFile::Open(std::string srcfile, Error* error)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InputIsoFile::Precache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
return m_reader->Precache(progress, error);
|
||||||
|
}
|
||||||
|
|
||||||
void InputIsoFile::Close()
|
void InputIsoFile::Close()
|
||||||
{
|
{
|
||||||
if (m_reader)
|
if (m_reader)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
|
class ProgressCallback;
|
||||||
|
|
||||||
enum isoType
|
enum isoType
|
||||||
{
|
{
|
||||||
|
@ -65,6 +66,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Open(std::string srcfile, Error* error);
|
bool Open(std::string srcfile, Error* error);
|
||||||
|
bool Precache(ProgressCallback* progress, Error* error);
|
||||||
void Close();
|
void Close();
|
||||||
bool Detect(bool readType = true);
|
bool Detect(bool readType = true);
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,15 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#include "ThreadedFileReader.h"
|
#include "ThreadedFileReader.h"
|
||||||
|
#include "Host.h"
|
||||||
|
|
||||||
|
#include "common/Error.h"
|
||||||
|
#include "common/HostSys.h"
|
||||||
|
#include "common/Path.h"
|
||||||
|
#include "common/ProgressCallback.h"
|
||||||
|
#include "common/SmallString.h"
|
||||||
#include "common/Threading.h"
|
#include "common/Threading.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
// Make sure buffer size is bigger than the cutoff where PCSX2 emulates a seek
|
// Make sure buffer size is bigger than the cutoff where PCSX2 emulates a seek
|
||||||
|
@ -244,6 +251,36 @@ bool ThreadedFileReader::TryCachedRead(void*& buffer, u64& offset, u32& size, co
|
||||||
return allDone;
|
return allDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ThreadedFileReader::Precache(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
CancelAndWaitUntilStopped();
|
||||||
|
progress->SetStatusText(SmallString::from_format("Precaching {}...", Path::GetFileName(m_filename)).c_str());
|
||||||
|
return Precache2(progress, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThreadedFileReader::Precache2(ProgressCallback* progress, Error* error)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Precaching is not supported for this file format.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThreadedFileReader::CheckAvailableMemoryForPrecaching(u64 required_size, Error* error)
|
||||||
|
{
|
||||||
|
// Don't allow precaching to use more than 50% of system memory.
|
||||||
|
// Hopefully nobody's running 2-4GB potatoes anymore....
|
||||||
|
const u64 memory_size = GetPhysicalMemory();
|
||||||
|
const u64 max_precache_size = memory_size / 2;
|
||||||
|
if (required_size > max_precache_size)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error,
|
||||||
|
TRANSLATE_FS("CDVD", "Required memory ({}GB) is the above the maximum allowed ({}GB)."),
|
||||||
|
required_size / _1gb, max_precache_size / _1gb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ThreadedFileReader::Open(std::string filename, Error* error)
|
bool ThreadedFileReader::Open(std::string filename, Error* error)
|
||||||
{
|
{
|
||||||
CancelAndWaitUntilStopped();
|
CancelAndWaitUntilStopped();
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
|
class ProgressCallback;
|
||||||
|
|
||||||
/// A file reader for use with compressed formats
|
/// A file reader for use with compressed formats
|
||||||
/// Calls decompression code on a separate thread to make a synchronous decompression API async
|
/// Calls decompression code on a separate thread to make a synchronous decompression API async
|
||||||
|
@ -42,8 +43,12 @@ protected:
|
||||||
virtual int ReadChunk(void* dst, s64 chunkID) = 0;
|
virtual int ReadChunk(void* dst, s64 chunkID) = 0;
|
||||||
/// AsyncFileReader open but ThreadedFileReader needs prep work first
|
/// AsyncFileReader open but ThreadedFileReader needs prep work first
|
||||||
virtual bool Open2(std::string filename, Error* error) = 0;
|
virtual bool Open2(std::string filename, Error* error) = 0;
|
||||||
|
/// AsyncFileReader precache but ThreadedFileReader needs prep work first
|
||||||
|
virtual bool Precache2(ProgressCallback* progress, Error* error);
|
||||||
/// AsyncFileReader close but ThreadedFileReader needs prep work first
|
/// AsyncFileReader close but ThreadedFileReader needs prep work first
|
||||||
virtual void Close2() = 0;
|
virtual void Close2() = 0;
|
||||||
|
/// Checks system memory, to ensure that precaching would not exceed a reasonable amount.
|
||||||
|
bool CheckAvailableMemoryForPrecaching(u64 required_size, Error* error);
|
||||||
|
|
||||||
ThreadedFileReader();
|
ThreadedFileReader();
|
||||||
|
|
||||||
|
@ -109,7 +114,9 @@ public:
|
||||||
|
|
||||||
virtual u32 GetBlockCount() const = 0;
|
virtual u32 GetBlockCount() const = 0;
|
||||||
|
|
||||||
|
|
||||||
bool Open(std::string filename, Error* error);
|
bool Open(std::string filename, Error* error);
|
||||||
|
bool Precache(ProgressCallback* progress, Error* error);
|
||||||
int ReadSync(void* pBuffer, u32 sector, u32 count);
|
int ReadSync(void* pBuffer, u32 sector, u32 count);
|
||||||
void BeginRead(void* pBuffer, u32 sector, u32 count);
|
void BeginRead(void* pBuffer, u32 sector, u32 count);
|
||||||
int FinishRead();
|
int FinishRead();
|
||||||
|
|
|
@ -1111,6 +1111,7 @@ struct Pcsx2Config
|
||||||
bool
|
bool
|
||||||
CdvdVerboseReads : 1, // enables cdvd read activity verbosely dumped to the console
|
CdvdVerboseReads : 1, // enables cdvd read activity verbosely dumped to the console
|
||||||
CdvdDumpBlocks : 1, // enables cdvd block dumping
|
CdvdDumpBlocks : 1, // enables cdvd block dumping
|
||||||
|
CdvdPrecache : 1, // enables cdvd precaching of compressed images
|
||||||
EnablePatches : 1, // enables patch detection and application
|
EnablePatches : 1, // enables patch detection and application
|
||||||
EnableCheats : 1, // enables cheat detection and application
|
EnableCheats : 1, // enables cheat detection and application
|
||||||
EnablePINE : 1, // enables inter-process communication
|
EnablePINE : 1, // enables inter-process communication
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class ProgressCallback;
|
||||||
class SettingsInterface;
|
class SettingsInterface;
|
||||||
|
|
||||||
namespace Host
|
namespace Host
|
||||||
|
@ -131,6 +132,9 @@ namespace Host
|
||||||
/// Sets host-specific default settings.
|
/// Sets host-specific default settings.
|
||||||
void SetDefaultUISettings(SettingsInterface& si);
|
void SetDefaultUISettings(SettingsInterface& si);
|
||||||
|
|
||||||
|
/// Creates a progress callback that displays in the host.
|
||||||
|
std::unique_ptr<ProgressCallback> CreateHostProgressCallback();
|
||||||
|
|
||||||
namespace Internal
|
namespace Internal
|
||||||
{
|
{
|
||||||
/// Retrieves the base settings layer. Must call with lock held.
|
/// Retrieves the base settings layer. Must call with lock held.
|
||||||
|
|
|
@ -3330,6 +3330,9 @@ void FullscreenUI::DrawEmulationSettingsPage()
|
||||||
"EmuCore/Speedhacks", "fastCDVD", false);
|
"EmuCore/Speedhacks", "fastCDVD", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawToggleSetting(bsi, FSUI_CSTR("Enable CDVD Precaching"), FSUI_CSTR("Loads the disc image into RAM before starting the virtual machine."),
|
||||||
|
"EmuCore", "CdvdPrecache", false);
|
||||||
|
|
||||||
MenuHeading(FSUI_CSTR("Frame Pacing/Latency Control"));
|
MenuHeading(FSUI_CSTR("Frame Pacing/Latency Control"));
|
||||||
|
|
||||||
bool optimal_frame_pacing = (bsi->GetIntValue("EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY) == 0);
|
bool optimal_frame_pacing = (bsi->GetIntValue("EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY) == 0);
|
||||||
|
@ -6949,6 +6952,8 @@ TRANSLATE_NOOP("FullscreenUI", "Enable Host Filesystem");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Enables access to files from the host: namespace in the virtual machine.");
|
TRANSLATE_NOOP("FullscreenUI", "Enables access to files from the host: namespace in the virtual machine.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Enable Fast CDVD");
|
TRANSLATE_NOOP("FullscreenUI", "Enable Fast CDVD");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Fast disc access, less loading times. Not recommended.");
|
TRANSLATE_NOOP("FullscreenUI", "Fast disc access, less loading times. Not recommended.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Enable CDVD Precaching");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Loads the disc image into RAM before starting the virtual machine.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Frame Pacing/Latency Control");
|
TRANSLATE_NOOP("FullscreenUI", "Frame Pacing/Latency Control");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Maximum Frame Latency");
|
TRANSLATE_NOOP("FullscreenUI", "Maximum Frame Latency");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Sets the number of frames which can be queued.");
|
TRANSLATE_NOOP("FullscreenUI", "Sets the number of frames which can be queued.");
|
||||||
|
|
|
@ -1706,6 +1706,7 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
|
||||||
|
|
||||||
SettingsWrapBitBool(CdvdVerboseReads);
|
SettingsWrapBitBool(CdvdVerboseReads);
|
||||||
SettingsWrapBitBool(CdvdDumpBlocks);
|
SettingsWrapBitBool(CdvdDumpBlocks);
|
||||||
|
SettingsWrapBitBool(CdvdPrecache);
|
||||||
SettingsWrapBitBool(EnablePatches);
|
SettingsWrapBitBool(EnablePatches);
|
||||||
SettingsWrapBitBool(EnableCheats);
|
SettingsWrapBitBool(EnableCheats);
|
||||||
SettingsWrapBitBool(EnablePINE);
|
SettingsWrapBitBool(EnablePINE);
|
||||||
|
|
|
@ -113,6 +113,7 @@ namespace VMManager
|
||||||
static void ReportGameChangeToHost();
|
static void ReportGameChangeToHost();
|
||||||
static bool HasBootedELF();
|
static bool HasBootedELF();
|
||||||
static bool HasValidOrInitializingVM();
|
static bool HasValidOrInitializingVM();
|
||||||
|
static void PrecacheCDVDFile();
|
||||||
|
|
||||||
static std::string GetCurrentSaveStateFileName(s32 slot);
|
static std::string GetCurrentSaveStateFileName(s32 slot);
|
||||||
static bool DoLoadState(const char* filename);
|
static bool DoLoadState(const char* filename);
|
||||||
|
@ -1223,6 +1224,27 @@ bool VMManager::AutoDetectSource(const std::string& filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMManager::PrecacheCDVDFile()
|
||||||
|
{
|
||||||
|
Error error;
|
||||||
|
std::unique_ptr<ProgressCallback> progress = Host::CreateHostProgressCallback();
|
||||||
|
if (!DoCDVDprecache(progress.get(), &error))
|
||||||
|
{
|
||||||
|
if (progress->IsCancelled())
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage("PrecacheCDVDFile", ICON_FA_COMPACT_DISC,
|
||||||
|
TRANSLATE_STR("VMManager", "CDVD precaching was cancelled."),
|
||||||
|
Host::OSD_WARNING_DURATION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage("PrecacheCDVDFile", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||||
|
fmt::format(TRANSLATE_FS("VMManager", "CDVD precaching failed: {}"), error.GetDescription()),
|
||||||
|
Host::OSD_ERROR_DURATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VMManager::Initialize(VMBootParameters boot_params)
|
bool VMManager::Initialize(VMBootParameters boot_params)
|
||||||
{
|
{
|
||||||
const Common::Timer init_timer;
|
const Common::Timer init_timer;
|
||||||
|
@ -1522,6 +1544,9 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
||||||
close_cdvd_files.Cancel();
|
close_cdvd_files.Cancel();
|
||||||
close_state.Cancel();
|
close_state.Cancel();
|
||||||
|
|
||||||
|
if (EmuConfig.CdvdPrecache)
|
||||||
|
PrecacheCDVDFile();
|
||||||
|
|
||||||
hwReset();
|
hwReset();
|
||||||
|
|
||||||
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||||
#include "pcsx2/Input/InputManager.h"
|
#include "pcsx2/Input/InputManager.h"
|
||||||
#include "pcsx2/VMManager.h"
|
#include "pcsx2/VMManager.h"
|
||||||
|
#include "common/ProgressCallback.h"
|
||||||
|
|
||||||
void Host::CommitBaseSettingChanges()
|
void Host::CommitBaseSettingChanges()
|
||||||
{
|
{
|
||||||
|
@ -33,6 +34,11 @@ void Host::SetDefaultUISettings(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ProgressCallback> Host::CreateHostProgressCallback()
|
||||||
|
{
|
||||||
|
return ProgressCallback::CreateNullProgressCallback();
|
||||||
|
}
|
||||||
|
|
||||||
void Host::ReportErrorAsync(const std::string_view title, const std::string_view message)
|
void Host::ReportErrorAsync(const std::string_view title, const std::string_view message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue