mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-06 09:22:35 +00:00
Merge branch 'main' into qt-style
This commit is contained in:
commit
bc7bde8bcd
@ -890,7 +890,8 @@ if (ENABLE_DISCORD_RPC)
|
|||||||
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
|
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
|
||||||
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD)
|
||||||
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
|
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ namespace Config {
|
|||||||
static bool isNeo = false;
|
static bool isNeo = false;
|
||||||
static bool isFullscreen = false;
|
static bool isFullscreen = false;
|
||||||
static bool playBGM = false;
|
static bool playBGM = false;
|
||||||
|
static bool isTrophyPopupDisabled = false;
|
||||||
static int BGMvolume = 50;
|
static int BGMvolume = 50;
|
||||||
static bool enableDiscordRPC = false;
|
static bool enableDiscordRPC = false;
|
||||||
static u32 screenWidth = 1280;
|
static u32 screenWidth = 1280;
|
||||||
@ -65,6 +66,8 @@ static bool vkCrashDiagnostic = false;
|
|||||||
static s16 cursorState = HideCursorState::Idle;
|
static s16 cursorState = HideCursorState::Idle;
|
||||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||||
static bool separateupdatefolder = false;
|
static bool separateupdatefolder = false;
|
||||||
|
static bool compatibilityData = false;
|
||||||
|
static bool checkCompatibilityOnStartup = false;
|
||||||
|
|
||||||
// Gui
|
// Gui
|
||||||
std::vector<std::filesystem::path> settings_install_dirs = {};
|
std::vector<std::filesystem::path> settings_install_dirs = {};
|
||||||
@ -97,6 +100,10 @@ bool isFullscreenMode() {
|
|||||||
return isFullscreen;
|
return isFullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getisTrophyPopupDisabled() {
|
||||||
|
return isTrophyPopupDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
bool getPlayBGM() {
|
bool getPlayBGM() {
|
||||||
return playBGM;
|
return playBGM;
|
||||||
}
|
}
|
||||||
@ -229,6 +236,14 @@ bool getSeparateUpdateEnabled() {
|
|||||||
return separateupdatefolder;
|
return separateupdatefolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getCompatibilityEnabled() {
|
||||||
|
return compatibilityData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getCheckCompatibilityOnStartup() {
|
||||||
|
return checkCompatibilityOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
void setGpuId(s32 selectedGpuId) {
|
void setGpuId(s32 selectedGpuId) {
|
||||||
gpuId = selectedGpuId;
|
gpuId = selectedGpuId;
|
||||||
}
|
}
|
||||||
@ -289,6 +304,10 @@ void setFullscreenMode(bool enable) {
|
|||||||
isFullscreen = enable;
|
isFullscreen = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setisTrophyPopupDisabled(bool disable) {
|
||||||
|
isTrophyPopupDisabled = disable;
|
||||||
|
}
|
||||||
|
|
||||||
void setPlayBGM(bool enable) {
|
void setPlayBGM(bool enable) {
|
||||||
playBGM = enable;
|
playBGM = enable;
|
||||||
}
|
}
|
||||||
@ -353,6 +372,14 @@ void setSeparateUpdateEnabled(bool use) {
|
|||||||
separateupdatefolder = use;
|
separateupdatefolder = use;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCompatibilityEnabled(bool use) {
|
||||||
|
compatibilityData = use;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCheckCompatibilityOnStartup(bool use) {
|
||||||
|
checkCompatibilityOnStartup = use;
|
||||||
|
}
|
||||||
|
|
||||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
|
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
|
||||||
main_window_geometry_x = x;
|
main_window_geometry_x = x;
|
||||||
main_window_geometry_y = y;
|
main_window_geometry_y = y;
|
||||||
@ -540,6 +567,7 @@ void load(const std::filesystem::path& path) {
|
|||||||
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
||||||
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
|
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
|
||||||
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
||||||
|
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
|
||||||
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
||||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||||
@ -553,6 +581,9 @@ void load(const std::filesystem::path& path) {
|
|||||||
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
||||||
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
||||||
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
||||||
|
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
|
||||||
|
checkCompatibilityOnStartup =
|
||||||
|
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.contains("Input")) {
|
if (data.contains("Input")) {
|
||||||
@ -656,6 +687,7 @@ void save(const std::filesystem::path& path) {
|
|||||||
|
|
||||||
data["General"]["isPS4Pro"] = isNeo;
|
data["General"]["isPS4Pro"] = isNeo;
|
||||||
data["General"]["Fullscreen"] = isFullscreen;
|
data["General"]["Fullscreen"] = isFullscreen;
|
||||||
|
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
|
||||||
data["General"]["playBGM"] = playBGM;
|
data["General"]["playBGM"] = playBGM;
|
||||||
data["General"]["BGMvolume"] = BGMvolume;
|
data["General"]["BGMvolume"] = BGMvolume;
|
||||||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||||
@ -666,6 +698,8 @@ void save(const std::filesystem::path& path) {
|
|||||||
data["General"]["showSplash"] = isShowSplash;
|
data["General"]["showSplash"] = isShowSplash;
|
||||||
data["General"]["autoUpdate"] = isAutoUpdate;
|
data["General"]["autoUpdate"] = isAutoUpdate;
|
||||||
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
||||||
|
data["General"]["compatibilityEnabled"] = compatibilityData;
|
||||||
|
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
||||||
data["Input"]["cursorState"] = cursorState;
|
data["Input"]["cursorState"] = cursorState;
|
||||||
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
||||||
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
||||||
@ -751,6 +785,7 @@ void saveMainWindow(const std::filesystem::path& path) {
|
|||||||
void setDefaultValues() {
|
void setDefaultValues() {
|
||||||
isNeo = false;
|
isNeo = false;
|
||||||
isFullscreen = false;
|
isFullscreen = false;
|
||||||
|
isTrophyPopupDisabled = false;
|
||||||
playBGM = false;
|
playBGM = false;
|
||||||
BGMvolume = 50;
|
BGMvolume = 50;
|
||||||
enableDiscordRPC = true;
|
enableDiscordRPC = true;
|
||||||
@ -787,6 +822,8 @@ void setDefaultValues() {
|
|||||||
m_language = 1;
|
m_language = 1;
|
||||||
gpuId = -1;
|
gpuId = -1;
|
||||||
separateupdatefolder = false;
|
separateupdatefolder = false;
|
||||||
|
compatibilityData = false;
|
||||||
|
checkCompatibilityOnStartup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
@ -19,8 +19,11 @@ bool isNeoMode();
|
|||||||
bool isFullscreenMode();
|
bool isFullscreenMode();
|
||||||
bool getPlayBGM();
|
bool getPlayBGM();
|
||||||
int getBGMvolume();
|
int getBGMvolume();
|
||||||
|
bool getisTrophyPopupDisabled();
|
||||||
bool getEnableDiscordRPC();
|
bool getEnableDiscordRPC();
|
||||||
bool getSeparateUpdateEnabled();
|
bool getSeparateUpdateEnabled();
|
||||||
|
bool getCompatibilityEnabled();
|
||||||
|
bool getCheckCompatibilityOnStartup();
|
||||||
|
|
||||||
std::string getLogFilter();
|
std::string getLogFilter();
|
||||||
std::string getLogType();
|
std::string getLogType();
|
||||||
@ -61,6 +64,7 @@ void setGpuId(s32 selectedGpuId);
|
|||||||
void setScreenWidth(u32 width);
|
void setScreenWidth(u32 width);
|
||||||
void setScreenHeight(u32 height);
|
void setScreenHeight(u32 height);
|
||||||
void setFullscreenMode(bool enable);
|
void setFullscreenMode(bool enable);
|
||||||
|
void setisTrophyPopupDisabled(bool disable);
|
||||||
void setPlayBGM(bool enable);
|
void setPlayBGM(bool enable);
|
||||||
void setBGMvolume(int volume);
|
void setBGMvolume(int volume);
|
||||||
void setEnableDiscordRPC(bool enable);
|
void setEnableDiscordRPC(bool enable);
|
||||||
@ -70,6 +74,8 @@ void setUserName(const std::string& type);
|
|||||||
void setUpdateChannel(const std::string& type);
|
void setUpdateChannel(const std::string& type);
|
||||||
void setSeparateUpdateEnabled(bool use);
|
void setSeparateUpdateEnabled(bool use);
|
||||||
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
|
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
|
||||||
|
void setCompatibilityEnabled(bool use);
|
||||||
|
void setCheckCompatibilityOnStartup(bool use);
|
||||||
|
|
||||||
void setCursorState(s16 cursorState);
|
void setCursorState(s16 cursorState);
|
||||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "splash.h"
|
#include "splash.h"
|
||||||
|
|
||||||
bool Splash::Open(const std::filesystem::path& filepath) {
|
bool Splash::Open(const std::filesystem::path& filepath) {
|
||||||
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");
|
ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed");
|
||||||
|
|
||||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
|
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
|
||||||
if (!file.IsOpen()) {
|
if (!file.IsOpen()) {
|
||||||
|
@ -171,6 +171,9 @@ void HandleTable::DeleteHandle(int d) {
|
|||||||
|
|
||||||
File* HandleTable::GetFile(int d) {
|
File* HandleTable::GetFile(int d) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
|
if (d < 0 || d >= m_files.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return m_files.at(d);
|
return m_files.at(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@ -13,7 +15,22 @@
|
|||||||
|
|
||||||
namespace Libraries::AudioOut {
|
namespace Libraries::AudioOut {
|
||||||
|
|
||||||
static std::unique_ptr<SDLAudioOut> audio;
|
struct PortOut {
|
||||||
|
void* impl;
|
||||||
|
u32 samples_num;
|
||||||
|
u32 freq;
|
||||||
|
OrbisAudioOutParamFormat format;
|
||||||
|
OrbisAudioOutPort type;
|
||||||
|
int channels_num;
|
||||||
|
bool is_float;
|
||||||
|
std::array<int, 8> volume;
|
||||||
|
u8 sample_size;
|
||||||
|
bool is_open;
|
||||||
|
};
|
||||||
|
std::shared_mutex ports_mutex;
|
||||||
|
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
|
||||||
|
|
||||||
|
static std::unique_ptr<AudioOutBackend> audio;
|
||||||
|
|
||||||
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
|
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
|
||||||
switch (port) {
|
switch (port) {
|
||||||
@ -70,6 +87,58 @@ static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case OrbisAudioOutParamFormat::S16Mono:
|
||||||
|
case OrbisAudioOutParamFormat::S16Stereo:
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
||||||
|
return false;
|
||||||
|
case OrbisAudioOutParamFormat::FloatMono:
|
||||||
|
case OrbisAudioOutParamFormat::FloatStereo:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case OrbisAudioOutParamFormat::S16Mono:
|
||||||
|
case OrbisAudioOutParamFormat::FloatMono:
|
||||||
|
return 1;
|
||||||
|
case OrbisAudioOutParamFormat::S16Stereo:
|
||||||
|
case OrbisAudioOutParamFormat::FloatStereo:
|
||||||
|
return 2;
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case OrbisAudioOutParamFormat::S16Mono:
|
||||||
|
case OrbisAudioOutParamFormat::S16Stereo:
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
||||||
|
return 2;
|
||||||
|
case OrbisAudioOutParamFormat::FloatMono:
|
||||||
|
case OrbisAudioOutParamFormat::FloatStereo:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH:
|
||||||
|
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
|
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
|
||||||
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
@ -110,8 +179,21 @@ int PS4_SYSV_ABI sceAudioOutChangeAppModuleState() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutClose() {
|
int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
|
||||||
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
LOG_INFO(Lib_AudioOut, "handle = {}", handle);
|
||||||
|
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::scoped_lock lock(ports_mutex);
|
||||||
|
auto& port = ports_out.at(handle - 1);
|
||||||
|
if (!port.is_open) {
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio->Close(port.impl);
|
||||||
|
port.impl = nullptr;
|
||||||
|
port.is_open = false;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,16 +262,21 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
|
|||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [type, channels_num] = audio->GetStatus(handle);
|
std::scoped_lock lock(ports_mutex);
|
||||||
|
const auto& port = ports_out.at(handle - 1);
|
||||||
|
if (!port.is_open) {
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
state->rerouteCounter = 0;
|
state->rerouteCounter = 0;
|
||||||
state->volume = 127;
|
state->volume = 127;
|
||||||
|
|
||||||
switch (type) {
|
switch (port.type) {
|
||||||
case OrbisAudioOutPort::Main:
|
case OrbisAudioOutPort::Main:
|
||||||
case OrbisAudioOutPort::Bgm:
|
case OrbisAudioOutPort::Bgm:
|
||||||
case OrbisAudioOutPort::Voice:
|
case OrbisAudioOutPort::Voice:
|
||||||
state->output = 1;
|
state->output = 1;
|
||||||
state->channel = (channels_num > 2 ? 2 : channels_num);
|
state->channel = port.channels_num > 2 ? 2 : port.channels_num;
|
||||||
break;
|
break;
|
||||||
case OrbisAudioOutPort::Personal:
|
case OrbisAudioOutPort::Personal:
|
||||||
case OrbisAudioOutPort::Padspk:
|
case OrbisAudioOutPort::Padspk:
|
||||||
@ -276,7 +363,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||||||
u32 sample_rate,
|
u32 sample_rate,
|
||||||
OrbisAudioOutParamExtendedInformation param_type) {
|
OrbisAudioOutParamExtendedInformation param_type) {
|
||||||
LOG_INFO(Lib_AudioOut,
|
LOG_INFO(Lib_AudioOut,
|
||||||
"AudioOutOpen id = {} port_type = {} index = {} lenght= {} sample_rate = {} "
|
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
|
||||||
"param_type = {} attr = {}",
|
"param_type = {} attr = {}",
|
||||||
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
|
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
|
||||||
GetAudioOutParamFormat(param_type.data_format),
|
GetAudioOutParamFormat(param_type.data_format),
|
||||||
@ -310,7 +397,26 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||||||
LOG_ERROR(Lib_AudioOut, "Invalid format attribute");
|
LOG_ERROR(Lib_AudioOut, "Invalid format attribute");
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
return audio->Open(port_type, length, sample_rate, format);
|
|
||||||
|
std::scoped_lock lock{ports_mutex};
|
||||||
|
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
|
||||||
|
if (port == ports_out.end()) {
|
||||||
|
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->is_open = true;
|
||||||
|
port->type = port_type;
|
||||||
|
port->samples_num = length;
|
||||||
|
port->freq = sample_rate;
|
||||||
|
port->format = format;
|
||||||
|
port->is_float = IsFormatFloat(format);
|
||||||
|
port->channels_num = GetFormatNumChannels(format);
|
||||||
|
port->sample_size = GetFormatSampleSize(format);
|
||||||
|
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
|
||||||
|
|
||||||
|
port->impl = audio->Open(port->is_float, port->channels_num, port->freq);
|
||||||
|
return std::distance(ports_out.begin(), port) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutOpenEx() {
|
int PS4_SYSV_ABI sceAudioOutOpenEx() {
|
||||||
@ -326,7 +432,15 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
|
|||||||
// Nothing to output
|
// Nothing to output
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
return audio->Output(handle, ptr);
|
|
||||||
|
auto& port = ports_out.at(handle - 1);
|
||||||
|
if (!port.is_open) {
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
|
||||||
|
audio->Output(port.impl, ptr, data_size);
|
||||||
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
|
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
|
||||||
@ -431,7 +545,42 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
|
|||||||
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
|
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
}
|
}
|
||||||
return audio->SetVolume(handle, flag, vol);
|
|
||||||
|
std::scoped_lock lock(ports_mutex);
|
||||||
|
auto& port = ports_out.at(handle - 1);
|
||||||
|
if (!port.is_open) {
|
||||||
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
|
||||||
|
auto bit = flag & 0x1u;
|
||||||
|
if (bit == 1) {
|
||||||
|
int src_index = i;
|
||||||
|
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
|
||||||
|
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
|
||||||
|
switch (i) {
|
||||||
|
case 4:
|
||||||
|
src_index = 6;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
src_index = 7;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
src_index = 4;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
src_index = 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port.volume[i] = vol[src_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio->SetVolume(port.impl, port.volume);
|
||||||
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutSetVolumeDown() {
|
int PS4_SYSV_ABI sceAudioOutSetVolumeDown() {
|
||||||
|
@ -64,7 +64,7 @@ int PS4_SYSV_ABI sceAudioOutA3dExit();
|
|||||||
int PS4_SYSV_ABI sceAudioOutA3dInit();
|
int PS4_SYSV_ABI sceAudioOutA3dInit();
|
||||||
int PS4_SYSV_ABI sceAudioOutAttachToApplicationByPid();
|
int PS4_SYSV_ABI sceAudioOutAttachToApplicationByPid();
|
||||||
int PS4_SYSV_ABI sceAudioOutChangeAppModuleState();
|
int PS4_SYSV_ABI sceAudioOutChangeAppModuleState();
|
||||||
int PS4_SYSV_ABI sceAudioOutClose();
|
int PS4_SYSV_ABI sceAudioOutClose(s32 handle);
|
||||||
int PS4_SYSV_ABI sceAudioOutDetachFromApplicationByPid();
|
int PS4_SYSV_ABI sceAudioOutDetachFromApplicationByPid();
|
||||||
int PS4_SYSV_ABI sceAudioOutExConfigureOutputMode();
|
int PS4_SYSV_ABI sceAudioOutExConfigureOutputMode();
|
||||||
int PS4_SYSV_ABI sceAudioOutExGetSystemInfo();
|
int PS4_SYSV_ABI sceAudioOutExGetSystemInfo();
|
||||||
|
19
src/core/libraries/audio/audioout_backend.h
Normal file
19
src/core/libraries/audio/audioout_backend.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Libraries::AudioOut {
|
||||||
|
|
||||||
|
class AudioOutBackend {
|
||||||
|
public:
|
||||||
|
AudioOutBackend() = default;
|
||||||
|
virtual ~AudioOutBackend() = default;
|
||||||
|
|
||||||
|
virtual void* Open(bool is_float, int num_channels, u32 sample_rate) = 0;
|
||||||
|
virtual void Close(void* impl) = 0;
|
||||||
|
virtual void Output(void* impl, const void* ptr, size_t size) = 0;
|
||||||
|
virtual void SetVolume(void* impl, std::array<int, 8> ch_volumes) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libraries::AudioOut
|
@ -1,141 +1,44 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <SDL3/SDL_audio.h>
|
#include <SDL3/SDL_audio.h>
|
||||||
#include <SDL3/SDL_init.h>
|
#include <SDL3/SDL_init.h>
|
||||||
#include <SDL3/SDL_timer.h>
|
#include <SDL3/SDL_timer.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/libraries/audio/audioout_error.h"
|
|
||||||
#include "core/libraries/audio/sdl_audio.h"
|
#include "core/libraries/audio/sdl_audio.h"
|
||||||
|
|
||||||
namespace Libraries::AudioOut {
|
namespace Libraries::AudioOut {
|
||||||
|
|
||||||
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
|
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
|
||||||
|
|
||||||
s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq,
|
void* SDLAudioOut::Open(bool is_float, int num_channels, u32 sample_rate) {
|
||||||
OrbisAudioOutParamFormat format) {
|
|
||||||
std::scoped_lock lock{m_mutex};
|
|
||||||
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
|
|
||||||
if (port == ports_out.end()) {
|
|
||||||
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
|
|
||||||
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
port->is_open = true;
|
|
||||||
port->type = type;
|
|
||||||
port->samples_num = samples_num;
|
|
||||||
port->freq = freq;
|
|
||||||
port->format = format;
|
|
||||||
SDL_AudioFormat sampleFormat;
|
|
||||||
switch (format) {
|
|
||||||
case OrbisAudioOutParamFormat::S16Mono:
|
|
||||||
sampleFormat = SDL_AUDIO_S16;
|
|
||||||
port->channels_num = 1;
|
|
||||||
port->sample_size = 2;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::FloatMono:
|
|
||||||
sampleFormat = SDL_AUDIO_F32;
|
|
||||||
port->channels_num = 1;
|
|
||||||
port->sample_size = 4;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::S16Stereo:
|
|
||||||
sampleFormat = SDL_AUDIO_S16;
|
|
||||||
port->channels_num = 2;
|
|
||||||
port->sample_size = 2;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::FloatStereo:
|
|
||||||
sampleFormat = SDL_AUDIO_F32;
|
|
||||||
port->channels_num = 2;
|
|
||||||
port->sample_size = 4;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH:
|
|
||||||
sampleFormat = SDL_AUDIO_S16;
|
|
||||||
port->channels_num = 8;
|
|
||||||
port->sample_size = 2;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH:
|
|
||||||
sampleFormat = SDL_AUDIO_F32;
|
|
||||||
port->channels_num = 8;
|
|
||||||
port->sample_size = 4;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
|
||||||
sampleFormat = SDL_AUDIO_S16;
|
|
||||||
port->channels_num = 8;
|
|
||||||
port->sample_size = 2;
|
|
||||||
break;
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
|
||||||
sampleFormat = SDL_AUDIO_F32;
|
|
||||||
port->channels_num = 8;
|
|
||||||
port->sample_size = 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE_MSG("Unknown format");
|
|
||||||
}
|
|
||||||
|
|
||||||
port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB);
|
|
||||||
|
|
||||||
SDL_AudioSpec fmt;
|
SDL_AudioSpec fmt;
|
||||||
SDL_zero(fmt);
|
SDL_zero(fmt);
|
||||||
fmt.format = sampleFormat;
|
fmt.format = is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16;
|
||||||
fmt.channels = port->channels_num;
|
fmt.channels = num_channels;
|
||||||
fmt.freq = freq;
|
fmt.freq = sample_rate;
|
||||||
port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
|
|
||||||
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream));
|
auto* stream =
|
||||||
return std::distance(ports_out.begin(), port) + 1;
|
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
||||||
|
SDL_ResumeAudioStreamDevice(stream);
|
||||||
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SDLAudioOut::Output(s32 handle, const void* ptr) {
|
void SDLAudioOut::Close(void* impl) {
|
||||||
auto& port = ports_out.at(handle - 1);
|
SDL_DestroyAudioStream(static_cast<SDL_AudioStream*>(impl));
|
||||||
if (!port.is_open) {
|
}
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
|
void SDLAudioOut::Output(void* impl, const void* ptr, size_t size) {
|
||||||
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
|
auto* stream = static_cast<SDL_AudioStream*>(impl);
|
||||||
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
|
SDL_PutAudioStreamData(stream, ptr, size);
|
||||||
|
while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
|
||||||
SDL_Delay(0);
|
SDL_Delay(0);
|
||||||
}
|
}
|
||||||
return result ? ORBIS_OK : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) {
|
void SDLAudioOut::SetVolume(void* impl, std::array<int, 8> ch_volumes) {
|
||||||
using Libraries::AudioOut::OrbisAudioOutParamFormat;
|
// Not yet implemented
|
||||||
auto& port = ports_out.at(handle - 1);
|
|
||||||
if (!port.is_open) {
|
|
||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
|
|
||||||
auto bit = bitflag & 0x1u;
|
|
||||||
|
|
||||||
if (bit == 1) {
|
|
||||||
int src_index = i;
|
|
||||||
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
|
|
||||||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
|
|
||||||
switch (i) {
|
|
||||||
case 4:
|
|
||||||
src_index = 6;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
src_index = 7;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
src_index = 4;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
src_index = 5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
port.volume[i] = volume[src_index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::AudioOut
|
} // namespace Libraries::AudioOut
|
||||||
|
@ -3,40 +3,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <shared_mutex>
|
#include "core/libraries/audio/audioout_backend.h"
|
||||||
#include <SDL3/SDL_audio.h>
|
|
||||||
#include "core/libraries/audio/audioout.h"
|
|
||||||
|
|
||||||
namespace Libraries::AudioOut {
|
namespace Libraries::AudioOut {
|
||||||
|
|
||||||
class SDLAudioOut {
|
class SDLAudioOut final : public AudioOutBackend {
|
||||||
public:
|
public:
|
||||||
explicit SDLAudioOut() = default;
|
void* Open(bool is_float, int num_channels, u32 sample_rate) override;
|
||||||
~SDLAudioOut() = default;
|
void Close(void* impl) override;
|
||||||
|
void Output(void* impl, const void* ptr, size_t size) override;
|
||||||
s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format);
|
void SetVolume(void* impl, std::array<int, 8> ch_volumes) override;
|
||||||
s32 Output(s32 handle, const void* ptr);
|
|
||||||
s32 SetVolume(s32 handle, s32 bitflag, s32* volume);
|
|
||||||
|
|
||||||
constexpr std::pair<OrbisAudioOutPort, int> GetStatus(s32 handle) const {
|
|
||||||
const auto& port = ports_out.at(handle - 1);
|
|
||||||
return std::make_pair(port.type, port.channels_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct PortOut {
|
|
||||||
SDL_AudioStream* stream;
|
|
||||||
u32 samples_num;
|
|
||||||
u32 freq;
|
|
||||||
OrbisAudioOutParamFormat format;
|
|
||||||
OrbisAudioOutPort type;
|
|
||||||
int channels_num;
|
|
||||||
std::array<int, 8> volume;
|
|
||||||
u8 sample_size;
|
|
||||||
bool is_open;
|
|
||||||
};
|
|
||||||
std::shared_mutex m_mutex;
|
|
||||||
std::array<PortOut, Libraries::AudioOut::SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libraries::AudioOut
|
} // namespace Libraries::AudioOut
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/config.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
#include "trophy_ui.h"
|
#include "trophy_ui.h"
|
||||||
@ -82,7 +83,10 @@ void TrophyUI::Draw() {
|
|||||||
|
|
||||||
void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::string& trophyName) {
|
void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::string& trophyName) {
|
||||||
std::lock_guard<std::mutex> lock(queueMtx);
|
std::lock_guard<std::mutex> lock(queueMtx);
|
||||||
if (current_trophy_ui.has_value()) {
|
|
||||||
|
if (Config::getisTrophyPopupDisabled()) {
|
||||||
|
return;
|
||||||
|
} else if (current_trophy_ui.has_value()) {
|
||||||
TrophyInfo new_trophy;
|
TrophyInfo new_trophy;
|
||||||
new_trophy.trophy_icon_path = trophyIconPath;
|
new_trophy.trophy_icon_path = trophyIconPath;
|
||||||
new_trophy.trophy_name = trophyName;
|
new_trophy.trophy_name = trophyName;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
#include "compatibility_info.h"
|
#include "compatibility_info.h"
|
||||||
@ -22,7 +23,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QNetworkReply* reply = FetchPage(1);
|
QNetworkReply* reply = FetchPage(1);
|
||||||
WaitForReply(reply);
|
if (!WaitForReply(reply))
|
||||||
|
return;
|
||||||
|
|
||||||
QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0,
|
QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0,
|
||||||
parent);
|
parent);
|
||||||
@ -57,12 +59,17 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply));
|
future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply));
|
||||||
connect(&future_watcher, &QFutureWatcher<QByteArray>::finished, [&]() {
|
connect(&future_watcher, &QFutureWatcher<void>::finished, [&]() {
|
||||||
for (int i = 0; i < remaining_pages; i++) {
|
for (int i = 0; i < remaining_pages; i++) {
|
||||||
|
if (replies[i]->bytesAvailable()) {
|
||||||
if (replies[i]->error() == QNetworkReply::NoError) {
|
if (replies[i]->error() == QNetworkReply::NoError) {
|
||||||
ExtractCompatibilityInfo(replies[i]->readAll());
|
ExtractCompatibilityInfo(replies[i]->readAll());
|
||||||
}
|
}
|
||||||
replies[i]->deleteLater();
|
replies[i]->deleteLater();
|
||||||
|
} else {
|
||||||
|
// This means the request timed out
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile compatibility_file(m_compatibility_filename);
|
QFile compatibility_file(m_compatibility_filename);
|
||||||
@ -83,6 +90,16 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
|
|||||||
|
|
||||||
dialog.reset();
|
dialog.reset();
|
||||||
});
|
});
|
||||||
|
connect(&future_watcher, &QFutureWatcher<void>::canceled, [&]() {
|
||||||
|
// Cleanup if user cancels pulling data
|
||||||
|
for (int i = 0; i < remaining_pages; i++) {
|
||||||
|
if (!replies[i]->bytesAvailable()) {
|
||||||
|
replies[i]->deleteLater();
|
||||||
|
} else if (!replies[i]->isFinished()) {
|
||||||
|
replies[i]->abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher<void>::cancel);
|
connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher<void>::cancel);
|
||||||
dialog.setRange(0, remaining_pages);
|
dialog.setRange(0, remaining_pages);
|
||||||
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
|
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
|
||||||
@ -105,20 +122,34 @@ QNetworkReply* CompatibilityInfoClass::FetchPage(int page_num) {
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) {
|
bool CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) {
|
||||||
|
// Returns true if reply succeeded, false if reply timed out
|
||||||
|
QTimer timer;
|
||||||
|
timer.setSingleShot(true);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
|
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
||||||
|
timer.start(5000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
return;
|
|
||||||
|
if (timer.isActive()) {
|
||||||
|
timer.stop();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||||
|
reply->abort();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) {
|
CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) {
|
||||||
QString title_id = QString::fromStdString(serial);
|
QString title_id = QString::fromStdString(serial);
|
||||||
if (m_compatibility_database.contains(title_id)) {
|
if (m_compatibility_database.contains(title_id)) {
|
||||||
{
|
{
|
||||||
|
QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
|
||||||
for (int os_int = 0; os_int != static_cast<int>(OSType::Last); os_int++) {
|
for (int os_int = 0; os_int != static_cast<int>(OSType::Last); os_int++) {
|
||||||
QString os_string = OSTypeToString.at(static_cast<OSType>(os_int));
|
QString os_string = OSTypeToString.at(static_cast<OSType>(os_int));
|
||||||
QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
|
|
||||||
if (compatibility_obj.contains(os_string)) {
|
if (compatibility_obj.contains(os_string)) {
|
||||||
QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject();
|
QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject();
|
||||||
CompatibilityEntry compatibility_entry{
|
CompatibilityEntry compatibility_entry{
|
||||||
@ -133,7 +164,9 @@ CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CompatibilityEntry{CompatibilityStatus::Unknown};
|
|
||||||
|
return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "",
|
||||||
|
0};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompatibilityInfoClass::LoadCompatibilityFile() {
|
bool CompatibilityInfoClass::LoadCompatibilityFile() {
|
||||||
|
@ -57,6 +57,7 @@ class CompatibilityInfoClass : public QObject {
|
|||||||
public:
|
public:
|
||||||
// Please think of a better alternative
|
// Please think of a better alternative
|
||||||
inline static const std::unordered_map<QString, CompatibilityStatus> LabelToCompatStatus = {
|
inline static const std::unordered_map<QString, CompatibilityStatus> LabelToCompatStatus = {
|
||||||
|
{QStringLiteral("status-unknown"), CompatibilityStatus::Unknown},
|
||||||
{QStringLiteral("status-nothing"), CompatibilityStatus::Nothing},
|
{QStringLiteral("status-nothing"), CompatibilityStatus::Nothing},
|
||||||
{QStringLiteral("status-boots"), CompatibilityStatus::Boots},
|
{QStringLiteral("status-boots"), CompatibilityStatus::Boots},
|
||||||
{QStringLiteral("status-menus"), CompatibilityStatus::Menus},
|
{QStringLiteral("status-menus"), CompatibilityStatus::Menus},
|
||||||
@ -87,7 +88,7 @@ public:
|
|||||||
bool LoadCompatibilityFile();
|
bool LoadCompatibilityFile();
|
||||||
CompatibilityEntry GetCompatibilityInfo(const std::string& serial);
|
CompatibilityEntry GetCompatibilityInfo(const std::string& serial);
|
||||||
void ExtractCompatibilityInfo(QByteArray response);
|
void ExtractCompatibilityInfo(QByteArray response);
|
||||||
static void WaitForReply(QNetworkReply* reply);
|
static bool WaitForReply(QNetworkReply* reply);
|
||||||
QNetworkReply* FetchPage(int page_num);
|
QNetworkReply* FetchPage(int page_num);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -80,6 +80,11 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
|
|||||||
QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url));
|
QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Do not show status column if it is not enabled
|
||||||
|
if (!Config::getCompatibilityEnabled()) {
|
||||||
|
this->setColumnHidden(2, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
|
void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
|
||||||
|
@ -198,7 +198,9 @@ void MainWindow::CreateDockWindows() {
|
|||||||
|
|
||||||
void MainWindow::LoadGameLists() {
|
void MainWindow::LoadGameLists() {
|
||||||
// Update compatibility database
|
// Update compatibility database
|
||||||
|
if (Config::getCheckCompatibilityOnStartup()) {
|
||||||
m_compat_info->UpdateCompatibilityDatabase(this);
|
m_compat_info->UpdateCompatibilityDatabase(this);
|
||||||
|
}
|
||||||
// Get game info from game folders.
|
// Get game info from game folders.
|
||||||
m_game_info->GetGameInfo(this);
|
m_game_info->GetGameInfo(this);
|
||||||
if (isTableList) {
|
if (isTableList) {
|
||||||
@ -600,7 +602,6 @@ void MainWindow::CreateConnects() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::StartGame() {
|
void MainWindow::StartGame() {
|
||||||
isGameRunning = true;
|
|
||||||
BackgroundMusicPlayer::getInstance().stopMusic();
|
BackgroundMusicPlayer::getInstance().stopMusic();
|
||||||
QString gamePath = "";
|
QString gamePath = "";
|
||||||
int table_mode = Config::getTableMode();
|
int table_mode = Config::getTableMode();
|
||||||
@ -623,13 +624,12 @@ void MainWindow::StartGame() {
|
|||||||
}
|
}
|
||||||
if (gamePath != "") {
|
if (gamePath != "") {
|
||||||
AddRecentFiles(gamePath);
|
AddRecentFiles(gamePath);
|
||||||
Core::Emulator emulator;
|
|
||||||
const auto path = Common::FS::PathFromQString(gamePath);
|
const auto path = Common::FS::PathFromQString(gamePath);
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
|
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emulator.Run(path);
|
StartEmulator(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,13 +726,12 @@ void MainWindow::BootGame() {
|
|||||||
QString(tr("Only one file can be selected!")));
|
QString(tr("Only one file can be selected!")));
|
||||||
} else {
|
} else {
|
||||||
std::filesystem::path path = Common::FS::PathFromQString(fileNames[0]);
|
std::filesystem::path path = Common::FS::PathFromQString(fileNames[0]);
|
||||||
Core::Emulator emulator;
|
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
QMessageBox::critical(nullptr, tr("Run Game"),
|
QMessageBox::critical(nullptr, tr("Run Game"),
|
||||||
QString(tr("Eboot.bin file not found")));
|
QString(tr("Eboot.bin file not found")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emulator.Run(path);
|
StartEmulator(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1113,12 +1112,11 @@ void MainWindow::CreateRecentGameActions() {
|
|||||||
connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) {
|
connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) {
|
||||||
auto gamePath = Common::FS::PathFromQString(action->text());
|
auto gamePath = Common::FS::PathFromQString(action->text());
|
||||||
AddRecentFiles(action->text()); // Update the list.
|
AddRecentFiles(action->text()); // Update the list.
|
||||||
Core::Emulator emulator;
|
|
||||||
if (!std::filesystem::exists(gamePath)) {
|
if (!std::filesystem::exists(gamePath)) {
|
||||||
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
|
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emulator.Run(gamePath);
|
StartEmulator(gamePath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,3 +1165,22 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) {
|
|||||||
}
|
}
|
||||||
return QMainWindow::eventFilter(obj, event);
|
return QMainWindow::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::StartEmulator(std::filesystem::path path) {
|
||||||
|
if (isGameRunning) {
|
||||||
|
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Game is already running!")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isGameRunning = true;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// SDL on macOS requires main thread.
|
||||||
|
Core::Emulator emulator;
|
||||||
|
emulator.Run(path);
|
||||||
|
#else
|
||||||
|
std::thread emulator_thread([=] {
|
||||||
|
Core::Emulator emulator;
|
||||||
|
emulator.Run(path);
|
||||||
|
});
|
||||||
|
emulator_thread.detach();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -72,6 +72,7 @@ private:
|
|||||||
void LoadTranslation();
|
void LoadTranslation();
|
||||||
void PlayBackgroundMusic();
|
void PlayBackgroundMusic();
|
||||||
QIcon RecolorIcon(const QIcon& icon, bool isWhite);
|
QIcon RecolorIcon(const QIcon& icon, bool isWhite);
|
||||||
|
void StartEmulator(std::filesystem::path);
|
||||||
bool isIconBlack = false;
|
bool isIconBlack = false;
|
||||||
bool isTableList = true;
|
bool isTableList = true;
|
||||||
bool isGameRunning = false;
|
bool isGameRunning = false;
|
||||||
|
@ -196,6 +196,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
|
|||||||
#endif
|
#endif
|
||||||
ui->GUIgroupBox->installEventFilter(this);
|
ui->GUIgroupBox->installEventFilter(this);
|
||||||
ui->widgetGroupBox->installEventFilter(this);
|
ui->widgetGroupBox->installEventFilter(this);
|
||||||
|
ui->disableTrophycheckBox->installEventFilter(this);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
ui->hideCursorGroupBox->installEventFilter(this);
|
ui->hideCursorGroupBox->installEventFilter(this);
|
||||||
@ -265,6 +266,8 @@ void SettingsDialog::LoadValuesFromConfig() {
|
|||||||
ui->dumpShadersCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "dumpShaders", false));
|
ui->dumpShadersCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "dumpShaders", false));
|
||||||
ui->nullGpuCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "nullGpu", false));
|
ui->nullGpuCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "nullGpu", false));
|
||||||
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
|
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
|
||||||
|
ui->disableTrophycheckBox->setChecked(
|
||||||
|
toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false));
|
||||||
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50));
|
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50));
|
||||||
ui->currentwidgetComboBox->setCurrentText(
|
ui->currentwidgetComboBox->setCurrentText(
|
||||||
QString::fromStdString(toml::find_or<std::string>(data, "GUI", "widgetStyle", "fusion")));
|
QString::fromStdString(toml::find_or<std::string>(data, "GUI", "widgetStyle", "fusion")));
|
||||||
@ -403,6 +406,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
|
|||||||
text = tr("GUIgroupBox");
|
text = tr("GUIgroupBox");
|
||||||
} else if (elementName == "widgetGroupBox") {
|
} else if (elementName == "widgetGroupBox") {
|
||||||
text = tr("widgetGroupBox");
|
text = tr("widgetGroupBox");
|
||||||
|
} else if (elementName == "disableTrophycheckBox") {
|
||||||
|
text = tr("disableTrophycheckBox");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
@ -491,6 +496,7 @@ void SettingsDialog::UpdateSettings() {
|
|||||||
Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]);
|
Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]);
|
||||||
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
|
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
|
||||||
Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked());
|
Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked());
|
||||||
|
Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked());
|
||||||
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
|
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
|
||||||
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
|
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
|
||||||
Config::setLogType(ui->logTypeComboBox->currentText().toStdString());
|
Config::setLogType(ui->logTypeComboBox->currentText().toStdString());
|
||||||
|
@ -59,9 +59,9 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>-97</y>
|
||||||
<width>822</width>
|
<width>815</width>
|
||||||
<height>554</height>
|
<height>618</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -469,6 +469,13 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>11</number>
|
<number>11</number>
|
||||||
</property>
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="disableTrophycheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Trophy Pop-ups</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="GUIMusicLayout">
|
<layout class="QVBoxLayout" name="GUIMusicLayout">
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
@ -561,16 +568,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="GUIwidgetSpacer" native="true">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>61</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -803,7 +800,7 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item alignment="Qt::AlignmentFlag::AlignHCenter">
|
<item>
|
||||||
<widget class="QSpinBox" name="idleTimeoutSpinBox">
|
<widget class="QSpinBox" name="idleTimeoutSpinBox">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -1206,6 +1206,11 @@
|
|||||||
<source>widgetGroupBox</source>
|
<source>widgetGroupBox</source>
|
||||||
<translation>Widget Styles:\nChoose the widget style to be applied at startup. Defaults to Fusion. "System" uses your system's widget style (Breeze, Oxygen, Kvantum etc). Requires an app restart to apply.</translation>
|
<translation>Widget Styles:\nChoose the widget style to be applied at startup. Defaults to Fusion. "System" uses your system's widget style (Breeze, Oxygen, Kvantum etc). Requires an app restart to apply.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../settings_dialog.cpp" line="267"/>
|
||||||
|
<source>disableTrophycheckBox</source>
|
||||||
|
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="450"/>
|
<location filename="../settings_dialog.cpp" line="450"/>
|
||||||
<source>hideCursorGroupBox</source>
|
<source>hideCursorGroupBox</source>
|
||||||
|
@ -428,6 +428,14 @@ struct Liverpool {
|
|||||||
BitField<0, 22, u32> tile_max;
|
BitField<0, 22, u32> tile_max;
|
||||||
} depth_slice;
|
} depth_slice;
|
||||||
|
|
||||||
|
bool DepthValid() const {
|
||||||
|
return Address() != 0 && z_info.format != ZFormat::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StencilValid() const {
|
||||||
|
return Address() != 0 && stencil_info.format != StencilFormat::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
u32 Pitch() const {
|
u32 Pitch() const {
|
||||||
return (depth_size.pitch_tile_max + 1) << 3;
|
return (depth_size.pitch_tile_max + 1) << 3;
|
||||||
}
|
}
|
||||||
@ -1275,6 +1283,26 @@ struct Liverpool {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 NumSamples() const {
|
||||||
|
// It seems that the number of samples > 1 set in the AA config doesn't mean we're
|
||||||
|
// always rendering with MSAA, so we need to derive MS ratio from the CB and DB
|
||||||
|
// settings.
|
||||||
|
u32 num_samples = 1u;
|
||||||
|
if (color_control.mode != ColorControl::OperationMode::Disable) {
|
||||||
|
for (auto cb = 0u; cb < NumColorBuffers; ++cb) {
|
||||||
|
const auto& col_buf = color_buffers[cb];
|
||||||
|
if (!col_buf) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
num_samples = std::max(num_samples, col_buf.NumSamples());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (depth_buffer.DepthValid() || depth_buffer.StencilValid()) {
|
||||||
|
num_samples = std::max(num_samples, depth_buffer.NumSamples());
|
||||||
|
}
|
||||||
|
return num_samples;
|
||||||
|
}
|
||||||
|
|
||||||
void SetDefaults();
|
void SetDefaults();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -235,25 +235,44 @@ bool BufferCache::BindVertexBuffers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
||||||
// Emulate QuadList primitive type with CPU made index buffer.
|
// Emulate QuadList and Polygon primitive types with CPU made index buffer.
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList && !is_indexed) {
|
if (!is_indexed) {
|
||||||
is_indexed = true;
|
bool needs_index_buffer = false;
|
||||||
|
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList ||
|
||||||
|
regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
||||||
|
needs_index_buffer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needs_index_buffer) {
|
||||||
|
return regs.num_indices;
|
||||||
|
}
|
||||||
|
|
||||||
// Emit indices.
|
// Emit indices.
|
||||||
const u32 index_size = 3 * regs.num_indices;
|
const u32 index_size = 3 * regs.num_indices;
|
||||||
const auto [data, offset] = stream_buffer.Map(index_size);
|
const auto [data, offset] = stream_buffer.Map(index_size);
|
||||||
|
|
||||||
|
switch (regs.primitive_type) {
|
||||||
|
case AmdGpu::PrimitiveType::QuadList:
|
||||||
Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices);
|
Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices);
|
||||||
|
break;
|
||||||
|
case AmdGpu::PrimitiveType::Polygon:
|
||||||
|
Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
stream_buffer.Commit();
|
stream_buffer.Commit();
|
||||||
|
|
||||||
// Bind index buffer.
|
// Bind index buffer.
|
||||||
|
is_indexed = true;
|
||||||
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16);
|
cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16);
|
||||||
return index_size / sizeof(u16);
|
return index_size / sizeof(u16);
|
||||||
}
|
}
|
||||||
if (!is_indexed) {
|
|
||||||
return regs.num_indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out index type and size.
|
// Figure out index type and size.
|
||||||
const bool is_index16 =
|
const bool is_index16 =
|
||||||
@ -288,6 +307,9 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
|||||||
cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type);
|
cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type);
|
||||||
return new_index_size / index_size;
|
return new_index_size / index_size;
|
||||||
}
|
}
|
||||||
|
if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
// Bind index buffer.
|
// Bind index buffer.
|
||||||
const u32 index_buffer_size = regs.num_indices * index_size;
|
const u32 index_buffer_size = regs.num_indices * index_size;
|
||||||
|
@ -117,6 +117,7 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) {
|
|||||||
case AmdGpu::PrimitiveType::PatchPrimitive:
|
case AmdGpu::PrimitiveType::PatchPrimitive:
|
||||||
return vk::PrimitiveTopology::ePatchList;
|
return vk::PrimitiveTopology::ePatchList;
|
||||||
case AmdGpu::PrimitiveType::QuadList:
|
case AmdGpu::PrimitiveType::QuadList:
|
||||||
|
case AmdGpu::PrimitiveType::Polygon:
|
||||||
// Needs to generate index buffer on the fly.
|
// Needs to generate index buffer on the fly.
|
||||||
return vk::PrimitiveTopology::eTriangleList;
|
return vk::PrimitiveTopology::eTriangleList;
|
||||||
case AmdGpu::PrimitiveType::RectList:
|
case AmdGpu::PrimitiveType::RectList:
|
||||||
|
@ -98,6 +98,15 @@ void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_ver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) {
|
||||||
|
u16* out_data = reinterpret_cast<u16*>(out_ptr);
|
||||||
|
for (u16 i = 1; i < num_vertices - 1; i++) {
|
||||||
|
*out_data++ = 0;
|
||||||
|
*out_data++ = i;
|
||||||
|
*out_data++ = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline vk::Format PromoteFormatToDepth(vk::Format fmt) {
|
static inline vk::Format PromoteFormatToDepth(vk::Format fmt) {
|
||||||
if (fmt == vk::Format::eR32Sfloat) {
|
if (fmt == vk::Format::eR32Sfloat) {
|
||||||
return vk::Format::eD32Sfloat;
|
return vk::Format::eD32Sfloat;
|
||||||
|
@ -85,10 +85,6 @@ public:
|
|||||||
return key.mrt_mask;
|
return key.mrt_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDepthEnabled() const {
|
|
||||||
return key.depth_stencil.depth_enable.Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsPrimitiveListTopology() const {
|
[[nodiscard]] bool IsPrimitiveListTopology() const {
|
||||||
return key.prim_type == AmdGpu::PrimitiveType::PointList ||
|
return key.prim_type == AmdGpu::PrimitiveType::PointList ||
|
||||||
key.prim_type == AmdGpu::PrimitiveType::LineList ||
|
key.prim_type == AmdGpu::PrimitiveType::LineList ||
|
||||||
|
@ -258,32 +258,28 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
auto& key = graphics_key;
|
auto& key = graphics_key;
|
||||||
|
|
||||||
key.depth_stencil = regs.depth_control;
|
key.depth_stencil = regs.depth_control;
|
||||||
|
key.stencil = regs.stencil_control;
|
||||||
key.depth_stencil.depth_write_enable.Assign(regs.depth_control.depth_write_enable.Value() &&
|
key.depth_stencil.depth_write_enable.Assign(regs.depth_control.depth_write_enable.Value() &&
|
||||||
!regs.depth_render_control.depth_clear_enable);
|
!regs.depth_render_control.depth_clear_enable);
|
||||||
key.depth_bias_enable = regs.polygon_control.NeedsBias();
|
key.depth_bias_enable = regs.polygon_control.NeedsBias();
|
||||||
|
|
||||||
const auto& db = regs.depth_buffer;
|
const auto depth_format = instance.GetSupportedFormat(
|
||||||
const auto ds_format = instance.GetSupportedFormat(
|
LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format,
|
||||||
LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format),
|
regs.depth_buffer.stencil_info.format),
|
||||||
vk::FormatFeatureFlagBits2::eDepthStencilAttachment);
|
vk::FormatFeatureFlagBits2::eDepthStencilAttachment);
|
||||||
if (db.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid) {
|
if (regs.depth_buffer.DepthValid()) {
|
||||||
key.depth_format = ds_format;
|
key.depth_format = depth_format;
|
||||||
} else {
|
} else {
|
||||||
key.depth_format = vk::Format::eUndefined;
|
key.depth_format = vk::Format::eUndefined;
|
||||||
|
key.depth_stencil.depth_enable.Assign(false);
|
||||||
}
|
}
|
||||||
if (regs.depth_control.depth_enable) {
|
if (regs.depth_buffer.StencilValid()) {
|
||||||
key.depth_stencil.depth_enable.Assign(key.depth_format != vk::Format::eUndefined);
|
key.stencil_format = depth_format;
|
||||||
}
|
|
||||||
key.stencil = regs.stencil_control;
|
|
||||||
|
|
||||||
if (db.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid) {
|
|
||||||
key.stencil_format = key.depth_format;
|
|
||||||
} else {
|
} else {
|
||||||
key.stencil_format = vk::Format::eUndefined;
|
key.stencil_format = vk::Format::eUndefined;
|
||||||
|
key.depth_stencil.stencil_enable.Assign(false);
|
||||||
}
|
}
|
||||||
if (key.depth_stencil.stencil_enable) {
|
|
||||||
key.depth_stencil.stencil_enable.Assign(key.stencil_format != vk::Format::eUndefined);
|
|
||||||
}
|
|
||||||
key.prim_type = regs.primitive_type;
|
key.prim_type = regs.primitive_type;
|
||||||
key.enable_primitive_restart = regs.enable_primitive_restart & 1;
|
key.enable_primitive_restart = regs.enable_primitive_restart & 1;
|
||||||
key.primitive_restart_index = regs.primitive_restart_index;
|
key.primitive_restart_index = regs.primitive_restart_index;
|
||||||
@ -291,7 +287,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
key.cull_mode = regs.polygon_control.CullingMode();
|
key.cull_mode = regs.polygon_control.CullingMode();
|
||||||
key.clip_space = regs.clipper_control.clip_space;
|
key.clip_space = regs.clipper_control.clip_space;
|
||||||
key.front_face = regs.polygon_control.front_face;
|
key.front_face = regs.polygon_control.front_face;
|
||||||
key.num_samples = regs.aa_config.NumSamples();
|
key.num_samples = regs.NumSamples();
|
||||||
|
|
||||||
const bool skip_cb_binding =
|
const bool skip_cb_binding =
|
||||||
regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable;
|
regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable;
|
||||||
@ -437,8 +433,6 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 num_samples = 1u;
|
|
||||||
|
|
||||||
// Second pass to fill remain CB pipeline key data
|
// Second pass to fill remain CB pipeline key data
|
||||||
for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
|
for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
|
||||||
auto const& col_buf = regs.color_buffers[cb];
|
auto const& col_buf = regs.color_buffers[cb];
|
||||||
@ -463,15 +457,8 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)};
|
key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)};
|
||||||
key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb));
|
key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb));
|
||||||
++remapped_cb;
|
++remapped_cb;
|
||||||
|
|
||||||
num_samples = std::max(num_samples, 1u << col_buf.attrib.num_samples_log2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// It seems that the number of samples > 1 set in the AA config doesn't mean we're always
|
|
||||||
// rendering with MSAA, so we need to derive MS ratio from the CB settings.
|
|
||||||
num_samples = std::max(num_samples, regs.depth_buffer.NumSamples());
|
|
||||||
key.num_samples = num_samples;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
||||||
|
@ -87,9 +87,11 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) {
|
|||||||
LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction");
|
LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool skip_cb_binding =
|
||||||
|
regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable;
|
||||||
for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) {
|
for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) {
|
||||||
const auto& col_buf = regs.color_buffers[col_buf_id];
|
const auto& col_buf = regs.color_buffers[col_buf_id];
|
||||||
if (!col_buf) {
|
if (skip_cb_binding || !col_buf) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,12 +136,8 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat;
|
if ((regs.depth_control.depth_enable && regs.depth_buffer.DepthValid()) ||
|
||||||
using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat;
|
(regs.depth_control.stencil_enable && regs.depth_buffer.StencilValid())) {
|
||||||
if (regs.depth_buffer.Address() != 0 &&
|
|
||||||
((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) ||
|
|
||||||
(regs.depth_control.stencil_enable &&
|
|
||||||
regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) {
|
|
||||||
const auto htile_address = regs.depth_htile_data_base.GetAddress();
|
const auto htile_address = regs.depth_htile_data_base.GetAddress();
|
||||||
const auto& hint = liverpool->last_db_extent;
|
const auto& hint = liverpool->last_db_extent;
|
||||||
auto& [image_id, desc] =
|
auto& [image_id, desc] =
|
||||||
@ -152,25 +150,36 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) {
|
|||||||
image.binding.is_target = 1u;
|
image.binding.is_target = 1u;
|
||||||
|
|
||||||
const auto slice = image_view.info.range.base.layer;
|
const auto slice = image_view.info.range.base.layer;
|
||||||
const bool is_clear = regs.depth_render_control.depth_clear_enable ||
|
const bool is_depth_clear = regs.depth_render_control.depth_clear_enable ||
|
||||||
texture_cache.IsMetaCleared(htile_address, slice);
|
texture_cache.IsMetaCleared(htile_address, slice);
|
||||||
|
const bool is_stencil_clear = regs.depth_render_control.stencil_clear_enable;
|
||||||
ASSERT(desc.view_info.range.extent.layers == 1);
|
ASSERT(desc.view_info.range.extent.layers == 1);
|
||||||
|
|
||||||
state.width = std::min<u32>(state.width, image.info.size.width);
|
state.width = std::min<u32>(state.width, image.info.size.width);
|
||||||
state.height = std::min<u32>(state.height, image.info.size.height);
|
state.height = std::min<u32>(state.height, image.info.size.height);
|
||||||
|
state.has_depth = regs.depth_buffer.DepthValid();
|
||||||
|
state.has_stencil = regs.depth_buffer.StencilValid();
|
||||||
|
if (state.has_depth) {
|
||||||
state.depth_attachment = {
|
state.depth_attachment = {
|
||||||
.imageView = *image_view.image_view,
|
.imageView = *image_view.image_view,
|
||||||
.imageLayout = vk::ImageLayout::eUndefined,
|
.imageLayout = vk::ImageLayout::eUndefined,
|
||||||
.loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
.loadOp =
|
||||||
|
is_depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
.clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear,
|
.clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear}},
|
||||||
.stencil = regs.stencil_clear}},
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
if (state.has_stencil) {
|
||||||
|
state.stencil_attachment = {
|
||||||
|
.imageView = *image_view.image_view,
|
||||||
|
.imageLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.loadOp =
|
||||||
|
is_stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.clearValue = vk::ClearValue{.depthStencil = {.stencil = regs.stencil_clear}},
|
||||||
|
};
|
||||||
|
}
|
||||||
texture_cache.TouchMeta(htile_address, slice, false);
|
texture_cache.TouchMeta(htile_address, slice, false);
|
||||||
state.has_depth =
|
|
||||||
regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid;
|
|
||||||
state.has_stencil = regs.depth_buffer.stencil_info.format !=
|
|
||||||
AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@ -246,11 +255,12 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) {
|
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList ||
|
||||||
// For QuadList we use generated index buffer to convert quads to triangles. Since it
|
regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
||||||
|
// We use a generated index buffer to convert quad lists and polygons to triangles. Since it
|
||||||
// changes type of the draw, arguments are not valid for this case. We need to run a
|
// changes type of the draw, arguments are not valid for this case. We need to run a
|
||||||
// conversion pass to repack the indirect arguments buffer first.
|
// conversion pass to repack the indirect arguments buffer first.
|
||||||
LOG_WARNING(Render_Vulkan, "QuadList primitive type is not supported for indirect draw");
|
LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,6 +787,7 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s
|
|||||||
desc.view_info.range);
|
desc.view_info.range);
|
||||||
}
|
}
|
||||||
state.depth_attachment.imageLayout = image.last_state.layout;
|
state.depth_attachment.imageLayout = image.last_state.layout;
|
||||||
|
state.stencil_attachment.imageLayout = image.last_state.layout;
|
||||||
image.usage.depth_target = true;
|
image.usage.depth_target = true;
|
||||||
image.usage.stencil = has_stencil;
|
image.usage.stencil = has_stencil;
|
||||||
}
|
}
|
||||||
@ -806,6 +817,38 @@ void Rasterizer::Resolve() {
|
|||||||
mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start;
|
mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start;
|
||||||
mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer;
|
mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer;
|
||||||
|
|
||||||
|
mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||||
|
mrt0_range);
|
||||||
|
|
||||||
|
mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||||
|
mrt1_range);
|
||||||
|
|
||||||
|
if (mrt0_image.info.num_samples == 1) {
|
||||||
|
// Vulkan does not allow resolve from a single sample image, so change it to a copy.
|
||||||
|
// Note that resolving a single-sampled image doesn't really make sense, but a game might do
|
||||||
|
// it.
|
||||||
|
vk::ImageCopy region = {
|
||||||
|
.srcSubresource =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = mrt0_range.base.layer,
|
||||||
|
.layerCount = mrt0_range.extent.layers,
|
||||||
|
},
|
||||||
|
.srcOffset = {0, 0, 0},
|
||||||
|
.dstSubresource =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = mrt1_range.base.layer,
|
||||||
|
.layerCount = mrt1_range.extent.layers,
|
||||||
|
},
|
||||||
|
.dstOffset = {0, 0, 0},
|
||||||
|
.extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1},
|
||||||
|
};
|
||||||
|
cmdbuf.copyImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image,
|
||||||
|
vk::ImageLayout::eTransferDstOptimal, region);
|
||||||
|
} else {
|
||||||
vk::ImageResolve region = {
|
vk::ImageResolve region = {
|
||||||
.srcSubresource =
|
.srcSubresource =
|
||||||
{
|
{
|
||||||
@ -825,15 +868,9 @@ void Rasterizer::Resolve() {
|
|||||||
.dstOffset = {0, 0, 0},
|
.dstOffset = {0, 0, 0},
|
||||||
.extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1},
|
.extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1},
|
||||||
};
|
};
|
||||||
|
cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||||
mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
mrt1_image.image, vk::ImageLayout::eTransferDstOptimal, region);
|
||||||
mrt0_range);
|
}
|
||||||
|
|
||||||
mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
|
||||||
mrt1_range);
|
|
||||||
|
|
||||||
cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image,
|
|
||||||
vk::ImageLayout::eTransferDstOptimal, region);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
|
void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
|
||||||
@ -989,6 +1026,10 @@ void Rasterizer::UpdateViewportScissorState() {
|
|||||||
enable_offset ? regs.window_offset.window_y_offset : 0);
|
enable_offset ? regs.window_offset.window_y_offset : 0);
|
||||||
|
|
||||||
for (u32 idx = 0; idx < Liverpool::NumViewports; idx++) {
|
for (u32 idx = 0; idx < Liverpool::NumViewports; idx++) {
|
||||||
|
if (regs.viewports[idx].xscale == 0) {
|
||||||
|
// Scissor and viewport counts should be equal.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto vp_scsr = scsr;
|
auto vp_scsr = scsr;
|
||||||
if (regs.mode_control.vport_scissor_enable) {
|
if (regs.mode_control.vport_scissor_enable) {
|
||||||
vp_scsr.top_left_x =
|
vp_scsr.top_left_x =
|
||||||
@ -1011,13 +1052,6 @@ void Rasterizer::UpdateViewportScissorState() {
|
|||||||
cmdbuf.setScissor(0, scissors);
|
cmdbuf.setScissor(0, scissors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rasterizer::UpdateDepthStencilState() {
|
|
||||||
auto& depth = liverpool->regs.depth_control;
|
|
||||||
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
|
||||||
cmdbuf.setDepthBoundsTestEnable(depth.depth_bounds_enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::ScopeMarkerBegin(const std::string_view& str) {
|
void Rasterizer::ScopeMarkerBegin(const std::string_view& str) {
|
||||||
if (Config::nullGpu() || !Config::vkMarkersEnabled()) {
|
if (Config::nullGpu() || !Config::vkMarkersEnabled()) {
|
||||||
return;
|
return;
|
||||||
|
@ -74,7 +74,6 @@ private:
|
|||||||
|
|
||||||
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
||||||
void UpdateViewportScissorState();
|
void UpdateViewportScissorState();
|
||||||
void UpdateDepthStencilState();
|
|
||||||
|
|
||||||
bool FilterDraw();
|
bool FilterDraw();
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) {
|
|||||||
is_rendering = true;
|
is_rendering = true;
|
||||||
render_state = new_state;
|
render_state = new_state;
|
||||||
|
|
||||||
const auto witdh =
|
const auto width =
|
||||||
render_state.width != std::numeric_limits<u32>::max() ? render_state.width : 1;
|
render_state.width != std::numeric_limits<u32>::max() ? render_state.width : 1;
|
||||||
const auto height =
|
const auto height =
|
||||||
render_state.height != std::numeric_limits<u32>::max() ? render_state.height : 1;
|
render_state.height != std::numeric_limits<u32>::max() ? render_state.height : 1;
|
||||||
@ -39,7 +39,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) {
|
|||||||
.renderArea =
|
.renderArea =
|
||||||
{
|
{
|
||||||
.offset = {0, 0},
|
.offset = {0, 0},
|
||||||
.extent = {witdh, height},
|
.extent = {width, height},
|
||||||
},
|
},
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
.colorAttachmentCount = render_state.num_color_attachments,
|
.colorAttachmentCount = render_state.num_color_attachments,
|
||||||
@ -47,7 +47,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) {
|
|||||||
? render_state.color_attachments.data()
|
? render_state.color_attachments.data()
|
||||||
: nullptr,
|
: nullptr,
|
||||||
.pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr,
|
.pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr,
|
||||||
.pStencilAttachment = render_state.has_stencil ? &render_state.depth_attachment : nullptr,
|
.pStencilAttachment = render_state.has_stencil ? &render_state.stencil_attachment : nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
current_cmdbuf.beginRendering(rendering_info);
|
current_cmdbuf.beginRendering(rendering_info);
|
||||||
|
@ -21,6 +21,7 @@ class Instance;
|
|||||||
struct RenderState {
|
struct RenderState {
|
||||||
std::array<vk::RenderingAttachmentInfo, 8> color_attachments{};
|
std::array<vk::RenderingAttachmentInfo, 8> color_attachments{};
|
||||||
vk::RenderingAttachmentInfo depth_attachment{};
|
vk::RenderingAttachmentInfo depth_attachment{};
|
||||||
|
vk::RenderingAttachmentInfo stencil_attachment{};
|
||||||
u32 num_color_attachments{};
|
u32 num_color_attachments{};
|
||||||
bool has_depth{};
|
bool has_depth{};
|
||||||
bool has_stencil{};
|
bool has_stencil{};
|
||||||
|
@ -266,7 +266,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer,
|
|||||||
props.is_tiled = buffer.IsTiled();
|
props.is_tiled = buffer.IsTiled();
|
||||||
tiling_mode = buffer.GetTilingMode();
|
tiling_mode = buffer.GetTilingMode();
|
||||||
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat());
|
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat());
|
||||||
num_samples = 1 << buffer.attrib.num_fragments_log2;
|
num_samples = buffer.NumSamples();
|
||||||
num_bits = NumBits(buffer.info.format);
|
num_bits = NumBits(buffer.info.format);
|
||||||
type = vk::ImageType::e2D;
|
type = vk::ImageType::e2D;
|
||||||
size.width = hint.Valid() ? hint.width : buffer.Pitch();
|
size.width = hint.Valid() ? hint.width : buffer.Pitch();
|
||||||
@ -289,7 +289,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice
|
|||||||
props.is_tiled = false;
|
props.is_tiled = false;
|
||||||
pixel_format = LiverpoolToVK::DepthFormat(buffer.z_info.format, buffer.stencil_info.format);
|
pixel_format = LiverpoolToVK::DepthFormat(buffer.z_info.format, buffer.stencil_info.format);
|
||||||
type = vk::ImageType::e2D;
|
type = vk::ImageType::e2D;
|
||||||
num_samples = 1 << buffer.z_info.num_samples; // spec doesn't say it is a log2
|
num_samples = buffer.NumSamples();
|
||||||
num_bits = buffer.NumBits();
|
num_bits = buffer.NumBits();
|
||||||
size.width = hint.Valid() ? hint.width : buffer.Pitch();
|
size.width = hint.Valid() ? hint.width : buffer.Pitch();
|
||||||
size.height = hint.Valid() ? hint.height : buffer.Height();
|
size.height = hint.Valid() ? hint.height : buffer.Height();
|
||||||
|
Loading…
Reference in New Issue
Block a user