early-access version 3512
This commit is contained in:
parent
fe509f4d90
commit
59516ad377
23 changed files with 2009 additions and 288 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3510.
|
This is the source code for early-access 3512.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -570,10 +570,10 @@ add_library(core STATIC
|
||||||
hle/service/nfp/nfp.h
|
hle/service/nfp/nfp.h
|
||||||
hle/service/nfp/nfp_device.cpp
|
hle/service/nfp/nfp_device.cpp
|
||||||
hle/service/nfp/nfp_device.h
|
hle/service/nfp/nfp_device.h
|
||||||
|
hle/service/nfp/nfp_interface.cpp
|
||||||
|
hle/service/nfp/nfp_interface.h
|
||||||
hle/service/nfp/nfp_result.h
|
hle/service/nfp/nfp_result.h
|
||||||
hle/service/nfp/nfp_types.h
|
hle/service/nfp/nfp_types.h
|
||||||
hle/service/nfp/nfp_user.cpp
|
|
||||||
hle/service/nfp/nfp_user.h
|
|
||||||
hle/service/ngct/ngct.cpp
|
hle/service/ngct/ngct.cpp
|
||||||
hle/service/ngct/ngct.h
|
hle/service/ngct/ngct.h
|
||||||
hle/service/nifm/nifm.cpp
|
hle/service/nifm/nifm.cpp
|
||||||
|
|
|
@ -49,12 +49,6 @@ public:
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
if (impl->GetSystem()
|
|
||||||
.Initialize(device_name, in_params, handle, applet_resource_user_id)
|
|
||||||
.IsError()) {
|
|
||||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~IAudioOut() override {
|
~IAudioOut() override {
|
||||||
|
@ -287,6 +281,13 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
|
||||||
|
|
||||||
auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
|
auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
|
||||||
in_params, handle, applet_resource_user_id);
|
in_params, handle, applet_resource_user_id);
|
||||||
|
result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle,
|
||||||
|
applet_resource_user_id);
|
||||||
|
if (result.IsError()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
impl->sessions[new_session_id] = audio_out->GetImpl();
|
impl->sessions[new_session_id] = audio_out->GetImpl();
|
||||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||||
|
|
|
@ -4,11 +4,138 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/nfp/nfp.h"
|
#include "core/hle/service/nfp/nfp.h"
|
||||||
#include "core/hle/service/nfp/nfp_user.h"
|
#include "core/hle/service/nfp/nfp_interface.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
|
|
||||||
namespace Service::NFP {
|
namespace Service::NFP {
|
||||||
|
|
||||||
|
class IUser final : public Interface {
|
||||||
|
public:
|
||||||
|
explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &IUser::Initialize, "Initialize"},
|
||||||
|
{1, &IUser::Finalize, "Finalize"},
|
||||||
|
{2, &IUser::ListDevices, "ListDevices"},
|
||||||
|
{3, &IUser::StartDetection, "StartDetection"},
|
||||||
|
{4, &IUser::StopDetection, "StopDetection"},
|
||||||
|
{5, &IUser::Mount, "Mount"},
|
||||||
|
{6, &IUser::Unmount, "Unmount"},
|
||||||
|
{7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
|
||||||
|
{8, &IUser::GetApplicationArea, "GetApplicationArea"},
|
||||||
|
{9, &IUser::SetApplicationArea, "SetApplicationArea"},
|
||||||
|
{10, &IUser::Flush, "Flush"},
|
||||||
|
{11, &IUser::Restore, "Restore"},
|
||||||
|
{12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
|
||||||
|
{13, &IUser::GetTagInfo, "GetTagInfo"},
|
||||||
|
{14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
|
||||||
|
{15, &IUser::GetCommonInfo, "GetCommonInfo"},
|
||||||
|
{16, &IUser::GetModelInfo, "GetModelInfo"},
|
||||||
|
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||||
|
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||||
|
{19, &IUser::GetState, "GetState"},
|
||||||
|
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
||||||
|
{21, &IUser::GetNpadId, "GetNpadId"},
|
||||||
|
{22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||||
|
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||||
|
{24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ISystem final : public Interface {
|
||||||
|
public:
|
||||||
|
explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &ISystem::InitializeSystem, "InitializeSystem"},
|
||||||
|
{1, &ISystem::FinalizeSystem, "FinalizeSystem"},
|
||||||
|
{2, &ISystem::ListDevices, "ListDevices"},
|
||||||
|
{3, &ISystem::StartDetection, "StartDetection"},
|
||||||
|
{4, &ISystem::StopDetection, "StopDetection"},
|
||||||
|
{5, &ISystem::Mount, "Mount"},
|
||||||
|
{6, &ISystem::Unmount, "Unmount"},
|
||||||
|
{10, &ISystem::Flush, "Flush"},
|
||||||
|
{11, &ISystem::Restore, "Restore"},
|
||||||
|
{12, &ISystem::CreateApplicationArea, "CreateApplicationArea"},
|
||||||
|
{13, &ISystem::GetTagInfo, "GetTagInfo"},
|
||||||
|
{14, &ISystem::GetRegisterInfo, "GetRegisterInfo"},
|
||||||
|
{15, &ISystem::GetCommonInfo, "GetCommonInfo"},
|
||||||
|
{16, &ISystem::GetModelInfo, "GetModelInfo"},
|
||||||
|
{17, &ISystem::AttachActivateEvent, "AttachActivateEvent"},
|
||||||
|
{18, &ISystem::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||||
|
{19, &ISystem::GetState, "GetState"},
|
||||||
|
{20, &ISystem::GetDeviceState, "GetDeviceState"},
|
||||||
|
{21, &ISystem::GetNpadId, "GetNpadId"},
|
||||||
|
{23, &ISystem::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||||
|
{100, &ISystem::Format, "Format"},
|
||||||
|
{101, &ISystem::GetAdminInfo, "GetAdminInfo"},
|
||||||
|
{102, &ISystem::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
|
||||||
|
{103, &ISystem::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
|
||||||
|
{104, &ISystem::DeleteRegisterInfo, "DeleteRegisterInfo"},
|
||||||
|
{105, &ISystem::DeleteApplicationArea, "DeleteApplicationArea"},
|
||||||
|
{106, &ISystem::ExistsApplicationArea, "ExistsApplicationArea"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IDebug final : public Interface {
|
||||||
|
public:
|
||||||
|
explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &IDebug::InitializeDebug, "InitializeDebug"},
|
||||||
|
{1, &IDebug::FinalizeDebug, "FinalizeDebug"},
|
||||||
|
{2, &IDebug::ListDevices, "ListDevices"},
|
||||||
|
{3, &IDebug::StartDetection, "StartDetection"},
|
||||||
|
{4, &IDebug::StopDetection, "StopDetection"},
|
||||||
|
{5, &IDebug::Mount, "Mount"},
|
||||||
|
{6, &IDebug::Unmount, "Unmount"},
|
||||||
|
{7, &IDebug::OpenApplicationArea, "OpenApplicationArea"},
|
||||||
|
{8, &IDebug::GetApplicationArea, "GetApplicationArea"},
|
||||||
|
{9, &IDebug::SetApplicationArea, "SetApplicationArea"},
|
||||||
|
{10, &IDebug::Flush, "Flush"},
|
||||||
|
{11, &IDebug::Restore, "Restore"},
|
||||||
|
{12, &IDebug::CreateApplicationArea, "CreateApplicationArea"},
|
||||||
|
{13, &IDebug::GetTagInfo, "GetTagInfo"},
|
||||||
|
{14, &IDebug::GetRegisterInfo, "GetRegisterInfo"},
|
||||||
|
{15, &IDebug::GetCommonInfo, "GetCommonInfo"},
|
||||||
|
{16, &IDebug::GetModelInfo, "GetModelInfo"},
|
||||||
|
{17, &IDebug::AttachActivateEvent, "AttachActivateEvent"},
|
||||||
|
{18, &IDebug::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||||
|
{19, &IDebug::GetState, "GetState"},
|
||||||
|
{20, &IDebug::GetDeviceState, "GetDeviceState"},
|
||||||
|
{21, &IDebug::GetNpadId, "GetNpadId"},
|
||||||
|
{22, &IDebug::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||||
|
{23, &IDebug::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||||
|
{24, &IDebug::RecreateApplicationArea, "RecreateApplicationArea"},
|
||||||
|
{100, &IDebug::Format, "Format"},
|
||||||
|
{101, &IDebug::GetAdminInfo, "GetAdminInfo"},
|
||||||
|
{102, &IDebug::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
|
||||||
|
{103, &IDebug::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
|
||||||
|
{104, &IDebug::DeleteRegisterInfo, "DeleteRegisterInfo"},
|
||||||
|
{105, &IDebug::DeleteApplicationArea, "DeleteApplicationArea"},
|
||||||
|
{106, &IDebug::ExistsApplicationArea, "ExistsApplicationArea"},
|
||||||
|
{200, &IDebug::GetAll, "GetAll"},
|
||||||
|
{201, &IDebug::SetAll, "SetAll"},
|
||||||
|
{202, &IDebug::FlushDebug, "FlushDebug"},
|
||||||
|
{203, &IDebug::BreakTag, "BreakTag"},
|
||||||
|
{204, nullptr, "ReadBackupData"},
|
||||||
|
{205, nullptr, "WriteBackupData"},
|
||||||
|
{206, nullptr, "WriteNtf"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class IUserManager final : public ServiceFramework<IUserManager> {
|
class IUserManager final : public ServiceFramework<IUserManager> {
|
||||||
public:
|
public:
|
||||||
explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
|
explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
|
||||||
|
@ -37,10 +164,68 @@ private:
|
||||||
std::shared_ptr<IUser> user_interface;
|
std::shared_ptr<IUser> user_interface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ISystemManager final : public ServiceFramework<ISystemManager> {
|
||||||
|
public:
|
||||||
|
explicit ISystemManager(Core::System& system_) : ServiceFramework{system_, "nfp:sys"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &ISystemManager::CreateSystemInterface, "CreateSystemInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateSystemInterface(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_NFP, "called");
|
||||||
|
|
||||||
|
if (system_interface == nullptr) {
|
||||||
|
system_interface = std::make_shared<ISystem>(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushIpcInterface<ISystem>(system_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ISystem> system_interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IDebugManager final : public ServiceFramework<IDebugManager> {
|
||||||
|
public:
|
||||||
|
explicit IDebugManager(Core::System& system_) : ServiceFramework{system_, "nfp:dbg"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &IDebugManager::CreateDebugInterface, "CreateDebugInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateDebugInterface(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_NFP, "called");
|
||||||
|
|
||||||
|
if (system_interface == nullptr) {
|
||||||
|
system_interface = std::make_shared<IDebug>(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushIpcInterface<IDebug>(system_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<IDebug> system_interface;
|
||||||
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("nfp:user", std::make_shared<IUserManager>(system));
|
server_manager->RegisterNamedService("nfp:user", std::make_shared<IUserManager>(system));
|
||||||
|
server_manager->RegisterNamedService("nfp:sys", std::make_shared<ISystemManager>(system));
|
||||||
|
server_manager->RegisterNamedService("nfp:dbg", std::make_shared<IDebugManager>(system));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include "core/hle/service/nfp/amiibo_crypto.h"
|
#include "core/hle/service/nfp/amiibo_crypto.h"
|
||||||
#include "core/hle/service/nfp/nfp_device.h"
|
#include "core/hle/service/nfp/nfp_device.h"
|
||||||
#include "core/hle/service/nfp/nfp_result.h"
|
#include "core/hle/service/nfp/nfp_result.h"
|
||||||
#include "core/hle/service/nfp/nfp_user.h"
|
|
||||||
#include "core/hle/service/time/time_manager.h"
|
#include "core/hle/service/time/time_manager.h"
|
||||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||||
#include "core/hle/service/time/time_zone_types.h"
|
#include "core/hle/service/time/time_zone_types.h"
|
||||||
|
@ -241,6 +240,42 @@ Result NfpDevice::Flush() {
|
||||||
|
|
||||||
tag_data.write_counter++;
|
tag_data.write_counter++;
|
||||||
|
|
||||||
|
FlushWithBreak(BreakType::Normal);
|
||||||
|
|
||||||
|
is_data_moddified = false;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::FlushDebug() {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_data.write_counter++;
|
||||||
|
|
||||||
|
FlushWithBreak(BreakType::Normal);
|
||||||
|
|
||||||
|
is_data_moddified = false;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::FlushWithBreak(BreakType break_type) {
|
||||||
|
if (break_type != BreakType::Normal) {
|
||||||
|
LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> data(sizeof(EncryptedNTAG215File));
|
std::vector<u8> data(sizeof(EncryptedNTAG215File));
|
||||||
if (is_plain_amiibo) {
|
if (is_plain_amiibo) {
|
||||||
memcpy(data.data(), &tag_data, sizeof(tag_data));
|
memcpy(data.data(), &tag_data, sizeof(tag_data));
|
||||||
|
@ -258,8 +293,6 @@ Result NfpDevice::Flush() {
|
||||||
return WriteAmiiboFailed;
|
return WriteAmiiboFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_data_moddified = false;
|
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,6 +450,38 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag_data.settings.settings.amiibo_initialized == 0) {
|
||||||
|
return RegistrationIsNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
Service::Mii::MiiManager manager;
|
||||||
|
const auto& settings = tag_data.settings;
|
||||||
|
|
||||||
|
// TODO: Validate and complete this data
|
||||||
|
register_info = {
|
||||||
|
.mii_store_data = {},
|
||||||
|
.creation_date = settings.init_date.GetWriteDate(),
|
||||||
|
.amiibo_name = GetAmiiboName(settings),
|
||||||
|
.font_region = settings.settings.font_region,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
|
Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
|
||||||
if (device_state != DeviceState::TagMounted) {
|
if (device_state != DeviceState::TagMounted) {
|
||||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
@ -807,6 +872,159 @@ Result NfpDevice::DeleteApplicationArea() {
|
||||||
return Flush();
|
return Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::ExistApplicationArea(bool& has_application_area) {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::GetAll(NfpData& data) const {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonInfo common_info{};
|
||||||
|
Service::Mii::MiiManager manager;
|
||||||
|
const u64 application_id = tag_data.application_id;
|
||||||
|
|
||||||
|
GetCommonInfo(common_info);
|
||||||
|
|
||||||
|
data = {
|
||||||
|
.magic = tag_data.constant_value,
|
||||||
|
.write_counter = tag_data.write_counter,
|
||||||
|
.settings_crc = tag_data.settings.crc,
|
||||||
|
.common_info = common_info,
|
||||||
|
.mii_char_info = tag_data.owner_mii,
|
||||||
|
.mii_store_data_extension = tag_data.mii_extension,
|
||||||
|
.creation_date = tag_data.settings.init_date.GetWriteDate(),
|
||||||
|
.amiibo_name = tag_data.settings.amiibo_name,
|
||||||
|
.amiibo_name_null_terminated = 0,
|
||||||
|
.settings = tag_data.settings.settings,
|
||||||
|
.unknown1 = tag_data.unknown,
|
||||||
|
.register_info_crc = tag_data.register_info_crc,
|
||||||
|
.unknown2 = tag_data.unknown2,
|
||||||
|
.application_id = application_id,
|
||||||
|
.access_id = tag_data.application_area_id,
|
||||||
|
.settings_crc_counter = tag_data.settings.crc_counter,
|
||||||
|
.font_region = tag_data.settings.settings.font_region,
|
||||||
|
.tag_type = PackedTagType::Type2,
|
||||||
|
.console_type =
|
||||||
|
static_cast<AppAreaVersion>(application_id >> application_id_version_offset & 0xf),
|
||||||
|
.application_id_byte = tag_data.application_id_byte,
|
||||||
|
.application_area = tag_data.application_area,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::SetAll(const NfpData& data) {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_data.constant_value = data.magic;
|
||||||
|
tag_data.write_counter = data.write_counter;
|
||||||
|
tag_data.settings.crc = data.settings_crc;
|
||||||
|
tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date);
|
||||||
|
tag_data.write_counter = data.common_info.write_counter;
|
||||||
|
tag_data.amiibo_version = data.common_info.version;
|
||||||
|
tag_data.owner_mii = data.mii_char_info;
|
||||||
|
tag_data.mii_extension = data.mii_store_data_extension;
|
||||||
|
tag_data.settings.init_date.SetWriteDate(data.creation_date);
|
||||||
|
tag_data.settings.amiibo_name = data.amiibo_name;
|
||||||
|
tag_data.settings.settings = data.settings;
|
||||||
|
tag_data.unknown = data.unknown1;
|
||||||
|
tag_data.register_info_crc = data.register_info_crc;
|
||||||
|
tag_data.unknown2 = data.unknown2;
|
||||||
|
tag_data.application_id = data.application_id;
|
||||||
|
tag_data.application_area_id = data.access_id;
|
||||||
|
tag_data.settings.crc_counter = data.settings_crc_counter;
|
||||||
|
tag_data.settings.settings.font_region.Assign(data.font_region);
|
||||||
|
tag_data.application_id_byte = data.application_id_byte;
|
||||||
|
tag_data.application_area = data.application_area;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::BreakTag(BreakType break_type) {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Complete this implementation
|
||||||
|
|
||||||
|
return FlushWithBreak(break_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::ReadBackupData() {
|
||||||
|
// Not implemented
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::WriteBackupData() {
|
||||||
|
// Not implemented
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::WriteNtf() {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
u64 NfpDevice::GetHandle() const {
|
u64 NfpDevice::GetHandle() const {
|
||||||
// Generate a handle based of the npad id
|
// Generate a handle based of the npad id
|
||||||
return static_cast<u64>(npad_id);
|
return static_cast<u64>(npad_id);
|
||||||
|
|
|
@ -41,12 +41,16 @@ public:
|
||||||
Result StopDetection();
|
Result StopDetection();
|
||||||
Result Mount(MountTarget mount_target);
|
Result Mount(MountTarget mount_target);
|
||||||
Result Unmount();
|
Result Unmount();
|
||||||
|
|
||||||
Result Flush();
|
Result Flush();
|
||||||
|
Result FlushDebug();
|
||||||
|
Result FlushWithBreak(BreakType break_type);
|
||||||
|
|
||||||
Result GetTagInfo(TagInfo& tag_info) const;
|
Result GetTagInfo(TagInfo& tag_info) const;
|
||||||
Result GetCommonInfo(CommonInfo& common_info) const;
|
Result GetCommonInfo(CommonInfo& common_info) const;
|
||||||
Result GetModelInfo(ModelInfo& model_info) const;
|
Result GetModelInfo(ModelInfo& model_info) const;
|
||||||
Result GetRegisterInfo(RegisterInfo& register_info) const;
|
Result GetRegisterInfo(RegisterInfo& register_info) const;
|
||||||
|
Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
|
||||||
Result GetAdminInfo(AdminInfo& admin_info) const;
|
Result GetAdminInfo(AdminInfo& admin_info) const;
|
||||||
|
|
||||||
Result DeleteRegisterInfo();
|
Result DeleteRegisterInfo();
|
||||||
|
@ -61,6 +65,14 @@ public:
|
||||||
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
|
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||||
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
|
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||||
Result DeleteApplicationArea();
|
Result DeleteApplicationArea();
|
||||||
|
Result ExistApplicationArea(bool& has_application_area);
|
||||||
|
|
||||||
|
Result GetAll(NfpData& data) const;
|
||||||
|
Result SetAll(const NfpData& data);
|
||||||
|
Result BreakTag(BreakType break_type);
|
||||||
|
Result ReadBackupData();
|
||||||
|
Result WriteBackupData();
|
||||||
|
Result WriteNtf();
|
||||||
|
|
||||||
u64 GetHandle() const;
|
u64 GetHandle() const;
|
||||||
u32 GetApplicationAreaSize() const;
|
u32 GetApplicationAreaSize() const;
|
||||||
|
|
1049
src/core/hle/service/nfp/nfp_interface.cpp
Executable file
1049
src/core/hle/service/nfp/nfp_interface.cpp
Executable file
File diff suppressed because it is too large
Load diff
81
src/core/hle/service/nfp/nfp_interface.h
Executable file
81
src/core/hle/service/nfp/nfp_interface.h
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Service::NFP {
|
||||||
|
class NfpDevice;
|
||||||
|
|
||||||
|
class Interface : public ServiceFramework<Interface> {
|
||||||
|
public:
|
||||||
|
explicit Interface(Core::System& system_, const char* name);
|
||||||
|
~Interface() override;
|
||||||
|
|
||||||
|
void Initialize(HLERequestContext& ctx);
|
||||||
|
void InitializeSystem(HLERequestContext& ctx);
|
||||||
|
void InitializeDebug(HLERequestContext& ctx);
|
||||||
|
void Finalize(HLERequestContext& ctx);
|
||||||
|
void FinalizeSystem(HLERequestContext& ctx);
|
||||||
|
void FinalizeDebug(HLERequestContext& ctx);
|
||||||
|
void ListDevices(HLERequestContext& ctx);
|
||||||
|
void StartDetection(HLERequestContext& ctx);
|
||||||
|
void StopDetection(HLERequestContext& ctx);
|
||||||
|
void Mount(HLERequestContext& ctx);
|
||||||
|
void Unmount(HLERequestContext& ctx);
|
||||||
|
void OpenApplicationArea(HLERequestContext& ctx);
|
||||||
|
void GetApplicationArea(HLERequestContext& ctx);
|
||||||
|
void SetApplicationArea(HLERequestContext& ctx);
|
||||||
|
void Flush(HLERequestContext& ctx);
|
||||||
|
void Restore(HLERequestContext& ctx);
|
||||||
|
void CreateApplicationArea(HLERequestContext& ctx);
|
||||||
|
void GetTagInfo(HLERequestContext& ctx);
|
||||||
|
void GetRegisterInfo(HLERequestContext& ctx);
|
||||||
|
void GetCommonInfo(HLERequestContext& ctx);
|
||||||
|
void GetModelInfo(HLERequestContext& ctx);
|
||||||
|
void AttachActivateEvent(HLERequestContext& ctx);
|
||||||
|
void AttachDeactivateEvent(HLERequestContext& ctx);
|
||||||
|
void GetState(HLERequestContext& ctx);
|
||||||
|
void GetDeviceState(HLERequestContext& ctx);
|
||||||
|
void GetNpadId(HLERequestContext& ctx);
|
||||||
|
void GetApplicationAreaSize(HLERequestContext& ctx);
|
||||||
|
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
|
||||||
|
void RecreateApplicationArea(HLERequestContext& ctx);
|
||||||
|
void Format(HLERequestContext& ctx);
|
||||||
|
void GetAdminInfo(HLERequestContext& ctx);
|
||||||
|
void GetRegisterInfoPrivate(HLERequestContext& ctx);
|
||||||
|
void SetRegisterInfoPrivate(HLERequestContext& ctx);
|
||||||
|
void DeleteRegisterInfo(HLERequestContext& ctx);
|
||||||
|
void DeleteApplicationArea(HLERequestContext& ctx);
|
||||||
|
void ExistsApplicationArea(HLERequestContext& ctx);
|
||||||
|
void GetAll(HLERequestContext& ctx);
|
||||||
|
void SetAll(HLERequestContext& ctx);
|
||||||
|
void FlushDebug(HLERequestContext& ctx);
|
||||||
|
void BreakTag(HLERequestContext& ctx);
|
||||||
|
void ReadBackupData(HLERequestContext& ctx);
|
||||||
|
void WriteBackupData(HLERequestContext& ctx);
|
||||||
|
void WriteNtf(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class State : u32 {
|
||||||
|
NonInitialized,
|
||||||
|
Initialized,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
|
||||||
|
|
||||||
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
|
||||||
|
std::array<std::shared_ptr<NfpDevice>, 10> devices{};
|
||||||
|
|
||||||
|
State state{State::NonInitialized};
|
||||||
|
Kernel::KEvent* availability_change_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::NFP
|
|
@ -109,6 +109,12 @@ enum class AppAreaVersion : u8 {
|
||||||
NotSet = 0xFF,
|
NotSet = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class BreakType : u32 {
|
||||||
|
Normal,
|
||||||
|
Unknown1,
|
||||||
|
Unknown2,
|
||||||
|
};
|
||||||
|
|
||||||
enum class CabinetMode : u8 {
|
enum class CabinetMode : u8 {
|
||||||
StartNicknameAndOwnerSettings,
|
StartNicknameAndOwnerSettings,
|
||||||
StartGameDataEraser,
|
StartGameDataEraser,
|
||||||
|
@ -181,6 +187,12 @@ struct AmiiboDate {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetWriteDate(const WriteDate& write_date) {
|
||||||
|
SetYear(write_date.year);
|
||||||
|
SetMonth(write_date.month);
|
||||||
|
SetDay(write_date.day);
|
||||||
|
}
|
||||||
|
|
||||||
void SetYear(u16 year) {
|
void SetYear(u16 year) {
|
||||||
const u16 year_converted = static_cast<u16>((year - 2000) << 9);
|
const u16 year_converted = static_cast<u16>((year - 2000) << 9);
|
||||||
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
|
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
|
||||||
|
@ -354,6 +366,15 @@ struct RegisterInfo {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
|
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
|
||||||
|
|
||||||
|
struct RegisterInfoPrivate {
|
||||||
|
Service::Mii::MiiStoreData mii_store_data;
|
||||||
|
WriteDate creation_date;
|
||||||
|
AmiiboName amiibo_name;
|
||||||
|
u8 font_region;
|
||||||
|
INSERT_PADDING_BYTES(0x8E);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
|
||||||
|
|
||||||
struct AdminInfo {
|
struct AdminInfo {
|
||||||
u64 application_id;
|
u64 application_id;
|
||||||
u32 application_area_id;
|
u32 application_area_id;
|
||||||
|
@ -366,6 +387,39 @@ struct AdminInfo {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");
|
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");
|
||||||
|
|
||||||
|
#pragma pack(1)
|
||||||
|
// This is nn::nfp::NfpData
|
||||||
|
struct NfpData {
|
||||||
|
u8 magic;
|
||||||
|
INSERT_PADDING_BYTES(0x1);
|
||||||
|
u8 write_counter;
|
||||||
|
INSERT_PADDING_BYTES(0x1);
|
||||||
|
u32 settings_crc;
|
||||||
|
INSERT_PADDING_BYTES(0x38);
|
||||||
|
CommonInfo common_info;
|
||||||
|
Service::Mii::Ver3StoreData mii_char_info;
|
||||||
|
Service::Mii::NfpStoreDataExtension mii_store_data_extension;
|
||||||
|
WriteDate creation_date;
|
||||||
|
std::array<u16_be, amiibo_name_length> amiibo_name;
|
||||||
|
u16 amiibo_name_null_terminated;
|
||||||
|
Settings settings;
|
||||||
|
u8 unknown1;
|
||||||
|
u32 register_info_crc;
|
||||||
|
std::array<u32, 5> unknown2;
|
||||||
|
INSERT_PADDING_BYTES(0x64);
|
||||||
|
u64 application_id;
|
||||||
|
u32 access_id;
|
||||||
|
u16 settings_crc_counter;
|
||||||
|
u8 font_region;
|
||||||
|
PackedTagType tag_type;
|
||||||
|
AppAreaVersion console_type;
|
||||||
|
u8 application_id_byte;
|
||||||
|
INSERT_PADDING_BYTES(0x2E);
|
||||||
|
ApplicationArea application_area;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
struct SectorKey {
|
struct SectorKey {
|
||||||
MifareCmd command;
|
MifareCmd command;
|
||||||
u8 unknown; // Usually 1
|
u8 unknown; // Usually 1
|
||||||
|
|
|
@ -174,8 +174,6 @@ add_library(video_core STATIC
|
||||||
renderer_vulkan/vk_master_semaphore.h
|
renderer_vulkan/vk_master_semaphore.h
|
||||||
renderer_vulkan/vk_pipeline_cache.cpp
|
renderer_vulkan/vk_pipeline_cache.cpp
|
||||||
renderer_vulkan/vk_pipeline_cache.h
|
renderer_vulkan/vk_pipeline_cache.h
|
||||||
renderer_vulkan/vk_present_manager.cpp
|
|
||||||
renderer_vulkan/vk_present_manager.h
|
|
||||||
renderer_vulkan/vk_query_cache.cpp
|
renderer_vulkan/vk_query_cache.cpp
|
||||||
renderer_vulkan/vk_query_cache.h
|
renderer_vulkan/vk_query_cache.h
|
||||||
renderer_vulkan/vk_rasterizer.cpp
|
renderer_vulkan/vk_rasterizer.cpp
|
||||||
|
|
|
@ -93,9 +93,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||||
state_tracker(), scheduler(device, state_tracker),
|
state_tracker(), scheduler(device, state_tracker),
|
||||||
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
|
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
|
||||||
render_window.GetFramebufferLayout().height, false),
|
render_window.GetFramebufferLayout().height, false),
|
||||||
present_manager(render_window, device, memory_allocator, scheduler, swapchain),
|
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler,
|
||||||
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
|
screen_info),
|
||||||
scheduler, screen_info),
|
|
||||||
rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
|
rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
|
||||||
state_tracker, scheduler) {
|
state_tracker, scheduler) {
|
||||||
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
|
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
|
||||||
|
@ -122,19 +121,46 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Update screen info if the framebuffer size has changed.
|
// Update screen info if the framebuffer size has changed.
|
||||||
screen_info.width = framebuffer->width;
|
if (screen_info.width != framebuffer->width || screen_info.height != framebuffer->height) {
|
||||||
screen_info.height = framebuffer->height;
|
screen_info.width = framebuffer->width;
|
||||||
|
screen_info.height = framebuffer->height;
|
||||||
|
}
|
||||||
const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
|
const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
|
||||||
const bool use_accelerated =
|
const bool use_accelerated =
|
||||||
rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
|
rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
|
||||||
const bool is_srgb = use_accelerated && screen_info.is_srgb;
|
const bool is_srgb = use_accelerated && screen_info.is_srgb;
|
||||||
RenderScreenshot(*framebuffer, use_accelerated);
|
RenderScreenshot(*framebuffer, use_accelerated);
|
||||||
|
|
||||||
Frame* frame = present_manager.GetRenderFrame();
|
bool has_been_recreated = false;
|
||||||
blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
|
const auto recreate_swapchain = [&](u32 width, u32 height) {
|
||||||
scheduler.Flush(*frame->render_ready);
|
if (!has_been_recreated) {
|
||||||
scheduler.Record([this, frame](vk::CommandBuffer) { present_manager.PushFrame(frame); });
|
has_been_recreated = true;
|
||||||
|
scheduler.Finish();
|
||||||
|
}
|
||||||
|
swapchain.Create(width, height, is_srgb);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||||
|
if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
|
||||||
|
swapchain.GetHeight() != layout.height) {
|
||||||
|
recreate_swapchain(layout.width, layout.height);
|
||||||
|
}
|
||||||
|
bool is_outdated;
|
||||||
|
do {
|
||||||
|
swapchain.AcquireNextImage();
|
||||||
|
is_outdated = swapchain.IsOutDated();
|
||||||
|
if (is_outdated) {
|
||||||
|
recreate_swapchain(layout.width, layout.height);
|
||||||
|
}
|
||||||
|
} while (is_outdated);
|
||||||
|
if (has_been_recreated) {
|
||||||
|
blit_screen.Recreate();
|
||||||
|
}
|
||||||
|
const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
|
||||||
|
const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
|
||||||
|
scheduler.Flush(render_semaphore, present_semaphore);
|
||||||
|
scheduler.WaitWorker();
|
||||||
|
swapchain.Present(render_semaphore);
|
||||||
|
|
||||||
gpu.RendererFrameEndNotify();
|
gpu.RendererFrameEndNotify();
|
||||||
rasterizer.TickFrame();
|
rasterizer.TickFrame();
|
||||||
|
@ -220,7 +246,8 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
|
||||||
});
|
});
|
||||||
const VkExtent2D render_area{.width = layout.width, .height = layout.height};
|
const VkExtent2D render_area{.width = layout.width, .height = layout.height};
|
||||||
const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
|
const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
|
||||||
blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated);
|
// Since we're not rendering to the screen, ignore the render semaphore.
|
||||||
|
void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
|
||||||
|
|
||||||
const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
|
const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
|
||||||
const VkBufferCreateInfo dst_buffer_info{
|
const VkBufferCreateInfo dst_buffer_info{
|
||||||
|
@ -243,7 +270,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "common/dynamic_library.h"
|
#include "common/dynamic_library.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||||
#include "video_core/renderer_vulkan/vk_present_manager.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||||
|
@ -77,7 +76,6 @@ private:
|
||||||
StateTracker state_tracker;
|
StateTracker state_tracker;
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
Swapchain swapchain;
|
Swapchain swapchain;
|
||||||
PresentManager present_manager;
|
|
||||||
BlitScreen blit_screen;
|
BlitScreen blit_screen;
|
||||||
RasterizerVulkan rasterizer;
|
RasterizerVulkan rasterizer;
|
||||||
std::optional<TurboMode> turbo_mode;
|
std::optional<TurboMode> turbo_mode;
|
||||||
|
|
|
@ -122,12 +122,10 @@ struct BlitScreen::BufferData {
|
||||||
|
|
||||||
BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_,
|
BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_,
|
||||||
const Device& device_, MemoryAllocator& memory_allocator_,
|
const Device& device_, MemoryAllocator& memory_allocator_,
|
||||||
Swapchain& swapchain_, PresentManager& present_manager_,
|
Swapchain& swapchain_, Scheduler& scheduler_, const ScreenInfo& screen_info_)
|
||||||
Scheduler& scheduler_, const ScreenInfo& screen_info_)
|
|
||||||
: cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_},
|
: cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_},
|
||||||
memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
|
memory_allocator{memory_allocator_}, swapchain{swapchain_}, scheduler{scheduler_},
|
||||||
scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_},
|
image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
|
||||||
current_srgb{swapchain.IsSrgb()}, image_view_format{swapchain.GetImageViewFormat()} {
|
|
||||||
resource_ticks.resize(image_count);
|
resource_ticks.resize(image_count);
|
||||||
|
|
||||||
CreateStaticResources();
|
CreateStaticResources();
|
||||||
|
@ -137,20 +135,25 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
|
||||||
BlitScreen::~BlitScreen() = default;
|
BlitScreen::~BlitScreen() = default;
|
||||||
|
|
||||||
void BlitScreen::Recreate() {
|
void BlitScreen::Recreate() {
|
||||||
present_manager.WaitPresent();
|
|
||||||
scheduler.Finish();
|
|
||||||
device.GetLogical().WaitIdle();
|
|
||||||
CreateDynamicResources();
|
CreateDynamicResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout,
|
const VkFramebuffer& host_framebuffer,
|
||||||
VkExtent2D render_area, bool use_accelerated) {
|
const Layout::FramebufferLayout layout, VkExtent2D render_area,
|
||||||
|
bool use_accelerated) {
|
||||||
RefreshResources(framebuffer);
|
RefreshResources(framebuffer);
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
|
||||||
|
if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) {
|
||||||
|
image_count = swapchain_images;
|
||||||
|
Recreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t image_index = swapchain.GetImageIndex();
|
||||||
|
|
||||||
scheduler.Wait(resource_ticks[image_index]);
|
scheduler.Wait(resource_ticks[image_index]);
|
||||||
resource_ticks[image_index] = scheduler.CurrentTick();
|
resource_ticks[image_index] = scheduler.CurrentTick();
|
||||||
|
|
||||||
|
@ -166,7 +169,7 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
std::memcpy(mapped_span.data(), &data, sizeof(data));
|
std::memcpy(mapped_span.data(), &data, sizeof(data));
|
||||||
|
|
||||||
if (!use_accelerated) {
|
if (!use_accelerated) {
|
||||||
const u64 image_offset = GetRawImageOffset(framebuffer);
|
const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
|
||||||
|
|
||||||
const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
|
const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
|
||||||
const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
|
const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
|
||||||
|
@ -201,8 +204,8 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
.depth = 1,
|
.depth = 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
|
scheduler.Record([this, copy, image_index](vk::CommandBuffer cmdbuf) {
|
||||||
const VkImage image = *raw_images[index];
|
const VkImage image = *raw_images[image_index];
|
||||||
const VkImageMemoryBarrier base_barrier{
|
const VkImageMemoryBarrier base_barrier{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
@ -242,15 +245,14 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
|
|
||||||
const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue();
|
const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue();
|
||||||
if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) {
|
if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) {
|
||||||
UpdateAADescriptorSet(source_image_view, false);
|
UpdateAADescriptorSet(image_index, source_image_view, false);
|
||||||
const u32 up_scale = Settings::values.resolution_info.up_scale;
|
const u32 up_scale = Settings::values.resolution_info.up_scale;
|
||||||
const u32 down_shift = Settings::values.resolution_info.down_shift;
|
const u32 down_shift = Settings::values.resolution_info.down_shift;
|
||||||
VkExtent2D size{
|
VkExtent2D size{
|
||||||
.width = (up_scale * framebuffer.width) >> down_shift,
|
.width = (up_scale * framebuffer.width) >> down_shift,
|
||||||
.height = (up_scale * framebuffer.height) >> down_shift,
|
.height = (up_scale * framebuffer.height) >> down_shift,
|
||||||
};
|
};
|
||||||
scheduler.Record([this, index = image_index, size,
|
scheduler.Record([this, image_index, size, anti_alias_pass](vk::CommandBuffer cmdbuf) {
|
||||||
anti_alias_pass](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkImageMemoryBarrier base_barrier{
|
const VkImageMemoryBarrier base_barrier{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
@ -324,7 +326,7 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
|
|
||||||
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
|
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0,
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0,
|
||||||
aa_descriptor_sets[index], {});
|
aa_descriptor_sets[image_index], {});
|
||||||
cmdbuf.Draw(4, 1, 0, 0);
|
cmdbuf.Draw(4, 1, 0, 0);
|
||||||
cmdbuf.EndRenderPass();
|
cmdbuf.EndRenderPass();
|
||||||
|
|
||||||
|
@ -367,99 +369,81 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
};
|
};
|
||||||
VkImageView fsr_image_view =
|
VkImageView fsr_image_view =
|
||||||
fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
|
fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
|
||||||
UpdateDescriptorSet(fsr_image_view, true);
|
UpdateDescriptorSet(image_index, fsr_image_view, true);
|
||||||
} else {
|
} else {
|
||||||
const bool is_nn =
|
const bool is_nn =
|
||||||
Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
|
Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
|
||||||
UpdateDescriptorSet(source_image_view, is_nn);
|
UpdateDescriptorSet(image_index, source_image_view, is_nn);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler.Record([this, host_framebuffer, index = image_index,
|
scheduler.Record(
|
||||||
size = render_area](vk::CommandBuffer cmdbuf) {
|
[this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) {
|
||||||
const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
|
const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
|
||||||
const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
|
const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
|
||||||
const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
|
const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
|
||||||
const VkClearValue clear_color{
|
const VkClearValue clear_color{
|
||||||
.color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
|
.color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
|
||||||
};
|
};
|
||||||
const VkRenderPassBeginInfo renderpass_bi{
|
const VkRenderPassBeginInfo renderpass_bi{
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.renderPass = *renderpass,
|
.renderPass = *renderpass,
|
||||||
.framebuffer = host_framebuffer,
|
.framebuffer = host_framebuffer,
|
||||||
.renderArea =
|
.renderArea =
|
||||||
{
|
{
|
||||||
.offset = {0, 0},
|
.offset = {0, 0},
|
||||||
.extent = size,
|
.extent = size,
|
||||||
},
|
},
|
||||||
.clearValueCount = 1,
|
.clearValueCount = 1,
|
||||||
.pClearValues = &clear_color,
|
.pClearValues = &clear_color,
|
||||||
};
|
};
|
||||||
const VkViewport viewport{
|
const VkViewport viewport{
|
||||||
.x = 0.0f,
|
.x = 0.0f,
|
||||||
.y = 0.0f,
|
.y = 0.0f,
|
||||||
.width = static_cast<float>(size.width),
|
.width = static_cast<float>(size.width),
|
||||||
.height = static_cast<float>(size.height),
|
.height = static_cast<float>(size.height),
|
||||||
.minDepth = 0.0f,
|
.minDepth = 0.0f,
|
||||||
.maxDepth = 1.0f,
|
.maxDepth = 1.0f,
|
||||||
};
|
};
|
||||||
const VkRect2D scissor{
|
const VkRect2D scissor{
|
||||||
.offset = {0, 0},
|
.offset = {0, 0},
|
||||||
.extent = size,
|
.extent = size,
|
||||||
};
|
};
|
||||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
auto graphics_pipeline = [this]() {
|
auto graphics_pipeline = [this]() {
|
||||||
switch (Settings::values.scaling_filter.GetValue()) {
|
switch (Settings::values.scaling_filter.GetValue()) {
|
||||||
case Settings::ScalingFilter::NearestNeighbor:
|
case Settings::ScalingFilter::NearestNeighbor:
|
||||||
case Settings::ScalingFilter::Bilinear:
|
case Settings::ScalingFilter::Bilinear:
|
||||||
return *bilinear_pipeline;
|
return *bilinear_pipeline;
|
||||||
case Settings::ScalingFilter::Bicubic:
|
case Settings::ScalingFilter::Bicubic:
|
||||||
return *bicubic_pipeline;
|
return *bicubic_pipeline;
|
||||||
case Settings::ScalingFilter::Gaussian:
|
case Settings::ScalingFilter::Gaussian:
|
||||||
return *gaussian_pipeline;
|
return *gaussian_pipeline;
|
||||||
case Settings::ScalingFilter::ScaleForce:
|
case Settings::ScalingFilter::ScaleForce:
|
||||||
return *scaleforce_pipeline;
|
return *scaleforce_pipeline;
|
||||||
default:
|
default:
|
||||||
return *bilinear_pipeline;
|
return *bilinear_pipeline;
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
||||||
cmdbuf.SetViewport(0, viewport);
|
cmdbuf.SetViewport(0, viewport);
|
||||||
cmdbuf.SetScissor(0, scissor);
|
cmdbuf.SetScissor(0, scissor);
|
||||||
|
|
||||||
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
|
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
|
||||||
descriptor_sets[index], {});
|
descriptor_sets[image_index], {});
|
||||||
cmdbuf.Draw(4, 1, 0, 0);
|
cmdbuf.Draw(4, 1, 0, 0);
|
||||||
cmdbuf.EndRenderPass();
|
cmdbuf.EndRenderPass();
|
||||||
});
|
});
|
||||||
|
return *semaphores[image_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
|
VkSemaphore BlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
|
||||||
bool use_accelerated, bool is_srgb) {
|
bool use_accelerated) {
|
||||||
// Recreate dynamic resources if the the image count or colorspace changed
|
const std::size_t image_index = swapchain.GetImageIndex();
|
||||||
if (const std::size_t swapchain_images = swapchain.GetImageCount();
|
const VkExtent2D render_area = swapchain.GetSize();
|
||||||
swapchain_images != image_count || current_srgb != is_srgb) {
|
|
||||||
current_srgb = is_srgb;
|
|
||||||
image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
|
||||||
image_count = swapchain_images;
|
|
||||||
Recreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recreate the presentation frame if the dimensions of the window changed
|
|
||||||
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||||
if (layout.width != frame->width || layout.height != frame->height ||
|
return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated);
|
||||||
is_srgb != frame->is_srgb) {
|
|
||||||
Recreate();
|
|
||||||
present_manager.RecreateFrame(frame, layout.width, layout.height, is_srgb,
|
|
||||||
image_view_format, *renderpass);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkExtent2D render_area{frame->width, frame->height};
|
|
||||||
Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated);
|
|
||||||
if (++image_index >= image_count) {
|
|
||||||
image_index = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
|
vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
|
||||||
|
@ -487,11 +471,13 @@ void BlitScreen::CreateStaticResources() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::CreateDynamicResources() {
|
void BlitScreen::CreateDynamicResources() {
|
||||||
|
CreateSemaphores();
|
||||||
CreateDescriptorPool();
|
CreateDescriptorPool();
|
||||||
CreateDescriptorSetLayout();
|
CreateDescriptorSetLayout();
|
||||||
CreateDescriptorSets();
|
CreateDescriptorSets();
|
||||||
CreatePipelineLayout();
|
CreatePipelineLayout();
|
||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
|
CreateFramebuffers();
|
||||||
CreateGraphicsPipeline();
|
CreateGraphicsPipeline();
|
||||||
fsr.reset();
|
fsr.reset();
|
||||||
smaa.reset();
|
smaa.reset();
|
||||||
|
@ -539,6 +525,11 @@ void BlitScreen::CreateShaders() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlitScreen::CreateSemaphores() {
|
||||||
|
semaphores.resize(image_count);
|
||||||
|
std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); });
|
||||||
|
}
|
||||||
|
|
||||||
void BlitScreen::CreateDescriptorPool() {
|
void BlitScreen::CreateDescriptorPool() {
|
||||||
const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
|
const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
|
||||||
{
|
{
|
||||||
|
@ -580,10 +571,10 @@ void BlitScreen::CreateDescriptorPool() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::CreateRenderPass() {
|
void BlitScreen::CreateRenderPass() {
|
||||||
renderpass = CreateRenderPassImpl(image_view_format);
|
renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
|
vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) {
|
||||||
const VkAttachmentDescription color_attachment{
|
const VkAttachmentDescription color_attachment{
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.format = format,
|
.format = format,
|
||||||
|
@ -593,7 +584,7 @@ vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
|
||||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.finalLayout = is_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VkAttachmentReference color_attachment_ref{
|
const VkAttachmentReference color_attachment_ref{
|
||||||
|
@ -1061,6 +1052,16 @@ void BlitScreen::CreateSampler() {
|
||||||
nn_sampler = device.GetLogical().CreateSampler(ci_nn);
|
nn_sampler = device.GetLogical().CreateSampler(ci_nn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlitScreen::CreateFramebuffers() {
|
||||||
|
const VkExtent2D size{swapchain.GetSize()};
|
||||||
|
framebuffers.resize(image_count);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < image_count; ++i) {
|
||||||
|
const VkImageView image_view{swapchain.GetImageViewIndex(i)};
|
||||||
|
framebuffers[i] = CreateFramebuffer(image_view, size, renderpass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BlitScreen::ReleaseRawImages() {
|
void BlitScreen::ReleaseRawImages() {
|
||||||
for (const u64 tick : resource_ticks) {
|
for (const u64 tick : resource_ticks) {
|
||||||
scheduler.Wait(tick);
|
scheduler.Wait(tick);
|
||||||
|
@ -1174,7 +1175,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||||
aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
|
aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer));
|
aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer), false);
|
||||||
aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
|
aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
|
||||||
|
|
||||||
const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
|
const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
|
||||||
|
@ -1318,7 +1319,8 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||||
aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
|
aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
|
void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view,
|
||||||
|
bool nn) const {
|
||||||
const VkDescriptorImageInfo image_info{
|
const VkDescriptorImageInfo image_info{
|
||||||
.sampler = nn ? *nn_sampler : *sampler,
|
.sampler = nn ? *nn_sampler : *sampler,
|
||||||
.imageView = image_view,
|
.imageView = image_view,
|
||||||
|
@ -1354,7 +1356,8 @@ void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
|
||||||
device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
|
device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
|
void BlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view,
|
||||||
|
bool nn) const {
|
||||||
const VkDescriptorBufferInfo buffer_info{
|
const VkDescriptorBufferInfo buffer_info{
|
||||||
.buffer = *buffer,
|
.buffer = *buffer,
|
||||||
.offset = offsetof(BufferData, uniform),
|
.offset = offsetof(BufferData, uniform),
|
||||||
|
@ -1477,7 +1480,8 @@ u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer)
|
||||||
return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
|
return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
|
u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
|
||||||
|
std::size_t image_index) const {
|
||||||
constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
|
constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
|
||||||
return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
|
return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
|
@ -43,9 +42,6 @@ class RasterizerVulkan;
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
class SMAA;
|
class SMAA;
|
||||||
class Swapchain;
|
class Swapchain;
|
||||||
class PresentManager;
|
|
||||||
|
|
||||||
struct Frame;
|
|
||||||
|
|
||||||
struct ScreenInfo {
|
struct ScreenInfo {
|
||||||
VkImage image{};
|
VkImage image{};
|
||||||
|
@ -59,17 +55,18 @@ class BlitScreen {
|
||||||
public:
|
public:
|
||||||
explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window,
|
explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window,
|
||||||
const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain,
|
const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain,
|
||||||
PresentManager& present_manager, Scheduler& scheduler,
|
Scheduler& scheduler, const ScreenInfo& screen_info);
|
||||||
const ScreenInfo& screen_info);
|
|
||||||
~BlitScreen();
|
~BlitScreen();
|
||||||
|
|
||||||
void Recreate();
|
void Recreate();
|
||||||
|
|
||||||
void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer,
|
[[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated);
|
const VkFramebuffer& host_framebuffer,
|
||||||
|
const Layout::FramebufferLayout layout, VkExtent2D render_area,
|
||||||
|
bool use_accelerated);
|
||||||
|
|
||||||
void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
|
[[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
|
||||||
bool use_accelerated, bool is_srgb);
|
bool use_accelerated);
|
||||||
|
|
||||||
[[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
|
[[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
|
||||||
VkExtent2D extent);
|
VkExtent2D extent);
|
||||||
|
@ -82,9 +79,10 @@ private:
|
||||||
|
|
||||||
void CreateStaticResources();
|
void CreateStaticResources();
|
||||||
void CreateShaders();
|
void CreateShaders();
|
||||||
|
void CreateSemaphores();
|
||||||
void CreateDescriptorPool();
|
void CreateDescriptorPool();
|
||||||
void CreateRenderPass();
|
void CreateRenderPass();
|
||||||
vk::RenderPass CreateRenderPassImpl(VkFormat format);
|
vk::RenderPass CreateRenderPassImpl(VkFormat, bool is_present = true);
|
||||||
void CreateDescriptorSetLayout();
|
void CreateDescriptorSetLayout();
|
||||||
void CreateDescriptorSets();
|
void CreateDescriptorSets();
|
||||||
void CreatePipelineLayout();
|
void CreatePipelineLayout();
|
||||||
|
@ -92,14 +90,15 @@ private:
|
||||||
void CreateSampler();
|
void CreateSampler();
|
||||||
|
|
||||||
void CreateDynamicResources();
|
void CreateDynamicResources();
|
||||||
|
void CreateFramebuffers();
|
||||||
|
|
||||||
void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
|
void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
|
||||||
void ReleaseRawImages();
|
void ReleaseRawImages();
|
||||||
void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
|
void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
|
||||||
void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
|
void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
|
||||||
|
|
||||||
void UpdateDescriptorSet(VkImageView image_view, bool nn) const;
|
void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const;
|
||||||
void UpdateAADescriptorSet(VkImageView image_view, bool nn) const;
|
void UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const;
|
||||||
void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
|
void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
|
||||||
void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
|
void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
|
||||||
const Layout::FramebufferLayout layout) const;
|
const Layout::FramebufferLayout layout) const;
|
||||||
|
@ -108,17 +107,16 @@ private:
|
||||||
void CreateFSR();
|
void CreateFSR();
|
||||||
|
|
||||||
u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
|
u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
|
||||||
u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
|
u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
|
||||||
|
std::size_t image_index) const;
|
||||||
|
|
||||||
Core::Memory::Memory& cpu_memory;
|
Core::Memory::Memory& cpu_memory;
|
||||||
Core::Frontend::EmuWindow& render_window;
|
Core::Frontend::EmuWindow& render_window;
|
||||||
const Device& device;
|
const Device& device;
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
Swapchain& swapchain;
|
Swapchain& swapchain;
|
||||||
PresentManager& present_manager;
|
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
std::size_t image_count;
|
std::size_t image_count;
|
||||||
std::size_t image_index{};
|
|
||||||
const ScreenInfo& screen_info;
|
const ScreenInfo& screen_info;
|
||||||
|
|
||||||
vk::ShaderModule vertex_shader;
|
vk::ShaderModule vertex_shader;
|
||||||
|
@ -137,6 +135,7 @@ private:
|
||||||
vk::Pipeline gaussian_pipeline;
|
vk::Pipeline gaussian_pipeline;
|
||||||
vk::Pipeline scaleforce_pipeline;
|
vk::Pipeline scaleforce_pipeline;
|
||||||
vk::RenderPass renderpass;
|
vk::RenderPass renderpass;
|
||||||
|
std::vector<vk::Framebuffer> framebuffers;
|
||||||
vk::DescriptorSets descriptor_sets;
|
vk::DescriptorSets descriptor_sets;
|
||||||
vk::Sampler nn_sampler;
|
vk::Sampler nn_sampler;
|
||||||
vk::Sampler sampler;
|
vk::Sampler sampler;
|
||||||
|
@ -146,6 +145,7 @@ private:
|
||||||
|
|
||||||
std::vector<u64> resource_ticks;
|
std::vector<u64> resource_ticks;
|
||||||
|
|
||||||
|
std::vector<vk::Semaphore> semaphores;
|
||||||
std::vector<vk::Image> raw_images;
|
std::vector<vk::Image> raw_images;
|
||||||
std::vector<vk::ImageView> raw_image_views;
|
std::vector<vk::ImageView> raw_image_views;
|
||||||
std::vector<MemoryCommit> raw_buffer_commits;
|
std::vector<MemoryCommit> raw_buffer_commits;
|
||||||
|
@ -164,8 +164,6 @@ private:
|
||||||
u32 raw_width = 0;
|
u32 raw_width = 0;
|
||||||
u32 raw_height = 0;
|
u32 raw_height = 0;
|
||||||
Service::android::PixelFormat pixel_format{};
|
Service::android::PixelFormat pixel_format{};
|
||||||
bool current_srgb;
|
|
||||||
VkFormat image_view_format;
|
|
||||||
|
|
||||||
std::unique_ptr<FSR> fsr;
|
std::unique_ptr<FSR> fsr;
|
||||||
std::unique_ptr<SMAA> smaa;
|
std::unique_ptr<SMAA> smaa;
|
||||||
|
|
|
@ -10,7 +10,14 @@
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
MasterSemaphore::MasterSemaphore(const Device& device) {
|
MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) {
|
||||||
|
if (!device.HasTimelineSemaphore()) {
|
||||||
|
static constexpr VkFenceCreateInfo fence_ci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0};
|
||||||
|
fence = device.GetLogical().CreateFence(fence_ci);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{
|
static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
@ -42,4 +49,134 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
|
||||||
|
|
||||||
MasterSemaphore::~MasterSemaphore() = default;
|
MasterSemaphore::~MasterSemaphore() = default;
|
||||||
|
|
||||||
|
void MasterSemaphore::Refresh() {
|
||||||
|
if (!semaphore) {
|
||||||
|
// If we don't support timeline semaphores, there's nothing to refresh
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 this_tick{};
|
||||||
|
u64 counter{};
|
||||||
|
do {
|
||||||
|
this_tick = gpu_tick.load(std::memory_order_acquire);
|
||||||
|
counter = semaphore.GetCounter();
|
||||||
|
if (counter < this_tick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
|
||||||
|
std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MasterSemaphore::Wait(u64 tick) {
|
||||||
|
if (!semaphore) {
|
||||||
|
// If we don't support timeline semaphores, use an atomic wait
|
||||||
|
while (true) {
|
||||||
|
u64 current_value = gpu_tick.load(std::memory_order_relaxed);
|
||||||
|
if (current_value >= tick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gpu_tick.wait(current_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to wait if the GPU is ahead of the tick
|
||||||
|
if (IsFree(tick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the GPU tick and try again
|
||||||
|
Refresh();
|
||||||
|
|
||||||
|
if (IsFree(tick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the above is hit, fallback to a regular wait
|
||||||
|
while (!semaphore.Wait(tick)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
|
||||||
|
VkSemaphore wait_semaphore, u64 host_tick) {
|
||||||
|
if (semaphore) {
|
||||||
|
return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
|
||||||
|
} else {
|
||||||
|
return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
||||||
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
|
||||||
|
VkSemaphore signal_semaphore,
|
||||||
|
VkSemaphore wait_semaphore, u64 host_tick) {
|
||||||
|
const VkSemaphore timeline_semaphore = *semaphore;
|
||||||
|
|
||||||
|
const u32 num_signal_semaphores = signal_semaphore ? 2 : 1;
|
||||||
|
const std::array signal_values{host_tick, u64(0)};
|
||||||
|
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
|
||||||
|
|
||||||
|
const u32 num_wait_semaphores = wait_semaphore ? 2 : 1;
|
||||||
|
const std::array wait_values{host_tick - 1, u64(1)};
|
||||||
|
const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
|
||||||
|
|
||||||
|
const VkTimelineSemaphoreSubmitInfo timeline_si{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.waitSemaphoreValueCount = num_wait_semaphores,
|
||||||
|
.pWaitSemaphoreValues = wait_values.data(),
|
||||||
|
.signalSemaphoreValueCount = num_signal_semaphores,
|
||||||
|
.pSignalSemaphoreValues = signal_values.data(),
|
||||||
|
};
|
||||||
|
const VkSubmitInfo submit_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.pNext = &timeline_si,
|
||||||
|
.waitSemaphoreCount = num_wait_semaphores,
|
||||||
|
.pWaitSemaphores = wait_semaphores.data(),
|
||||||
|
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = cmdbuf.address(),
|
||||||
|
.signalSemaphoreCount = num_signal_semaphores,
|
||||||
|
.pSignalSemaphores = signal_semaphores.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return device.GetGraphicsQueue().Submit(submit_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
|
||||||
|
VkSemaphore wait_semaphore, u64 host_tick) {
|
||||||
|
const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
|
||||||
|
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
|
||||||
|
|
||||||
|
const VkSubmitInfo submit_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.waitSemaphoreCount = num_wait_semaphores,
|
||||||
|
.pWaitSemaphores = &wait_semaphore,
|
||||||
|
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = cmdbuf.address(),
|
||||||
|
.signalSemaphoreCount = num_signal_semaphores,
|
||||||
|
.pSignalSemaphores = &signal_semaphore,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = device.GetGraphicsQueue().Submit(submit_info, *fence);
|
||||||
|
|
||||||
|
if (result == VK_SUCCESS) {
|
||||||
|
fence.Wait();
|
||||||
|
fence.Reset();
|
||||||
|
gpu_tick.store(host_tick);
|
||||||
|
gpu_tick.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -29,11 +31,6 @@ public:
|
||||||
return gpu_tick.load(std::memory_order_acquire);
|
return gpu_tick.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the timeline semaphore handle.
|
|
||||||
[[nodiscard]] VkSemaphore Handle() const noexcept {
|
|
||||||
return *semaphore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true when a tick has been hit by the GPU.
|
/// Returns true when a tick has been hit by the GPU.
|
||||||
[[nodiscard]] bool IsFree(u64 tick) const noexcept {
|
[[nodiscard]] bool IsFree(u64 tick) const noexcept {
|
||||||
return KnownGpuTick() >= tick;
|
return KnownGpuTick() >= tick;
|
||||||
|
@ -45,37 +42,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh the known GPU tick
|
/// Refresh the known GPU tick
|
||||||
void Refresh() {
|
void Refresh();
|
||||||
u64 this_tick{};
|
|
||||||
u64 counter{};
|
|
||||||
do {
|
|
||||||
this_tick = gpu_tick.load(std::memory_order_acquire);
|
|
||||||
counter = semaphore.GetCounter();
|
|
||||||
if (counter < this_tick) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
|
|
||||||
std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits for a tick to be hit on the GPU
|
/// Waits for a tick to be hit on the GPU
|
||||||
void Wait(u64 tick) {
|
void Wait(u64 tick);
|
||||||
// No need to wait if the GPU is ahead of the tick
|
|
||||||
if (IsFree(tick)) {
|
/// Submits the device graphics queue, updating the tick as necessary
|
||||||
return;
|
VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
|
||||||
}
|
VkSemaphore wait_semaphore, u64 host_tick);
|
||||||
// Update the GPU tick and try again
|
|
||||||
Refresh();
|
|
||||||
if (IsFree(tick)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If none of the above is hit, fallback to a regular wait
|
|
||||||
while (!semaphore.Wait(tick)) {
|
|
||||||
}
|
|
||||||
Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
|
||||||
|
VkSemaphore wait_semaphore, u64 host_tick);
|
||||||
|
VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
|
||||||
|
VkSemaphore wait_semaphore, u64 host_tick);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Device& device; ///< Device.
|
||||||
|
vk::Fence fence; ///< Fence.
|
||||||
vk::Semaphore semaphore; ///< Timeline semaphore.
|
vk::Semaphore semaphore; ///< Timeline semaphore.
|
||||||
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
|
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
|
||||||
std::atomic<u64> current_tick{1}; ///< Current logical tick.
|
std::atomic<u64> current_tick{1}; ///< Current logical tick.
|
||||||
|
|
|
@ -46,11 +46,10 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
|
||||||
|
|
||||||
Scheduler::~Scheduler() = default;
|
Scheduler::~Scheduler() = default;
|
||||||
|
|
||||||
u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||||
// When flushing, we only send data to the worker thread; no waiting is necessary.
|
// When flushing, we only send data to the worker thread; no waiting is necessary.
|
||||||
const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore);
|
SubmitExecution(signal_semaphore, wait_semaphore);
|
||||||
AllocateNewContext();
|
AllocateNewContext();
|
||||||
return signal_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||||
|
@ -206,53 +205,20 @@ void Scheduler::AllocateWorkerCommandBuffer() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||||
EndPendingOperations();
|
EndPendingOperations();
|
||||||
InvalidateState();
|
InvalidateState();
|
||||||
|
|
||||||
const u64 signal_value = master_semaphore->NextTick();
|
const u64 signal_value = master_semaphore->NextTick();
|
||||||
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
|
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
|
||||||
cmdbuf.End();
|
cmdbuf.End();
|
||||||
const VkSemaphore timeline_semaphore = master_semaphore->Handle();
|
|
||||||
|
|
||||||
const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
|
|
||||||
const std::array signal_values{signal_value, u64(0)};
|
|
||||||
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
|
|
||||||
|
|
||||||
const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
|
|
||||||
const std::array wait_values{signal_value - 1, u64(1)};
|
|
||||||
const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
|
|
||||||
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
|
||||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkTimelineSemaphoreSubmitInfo timeline_si{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.waitSemaphoreValueCount = num_wait_semaphores,
|
|
||||||
.pWaitSemaphoreValues = wait_values.data(),
|
|
||||||
.signalSemaphoreValueCount = num_signal_semaphores,
|
|
||||||
.pSignalSemaphoreValues = signal_values.data(),
|
|
||||||
};
|
|
||||||
const VkSubmitInfo submit_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
||||||
.pNext = &timeline_si,
|
|
||||||
.waitSemaphoreCount = num_wait_semaphores,
|
|
||||||
.pWaitSemaphores = wait_semaphores.data(),
|
|
||||||
.pWaitDstStageMask = wait_stage_masks.data(),
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = cmdbuf.address(),
|
|
||||||
.signalSemaphoreCount = num_signal_semaphores,
|
|
||||||
.pSignalSemaphores = signal_semaphores.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (on_submit) {
|
if (on_submit) {
|
||||||
on_submit();
|
on_submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock{submit_mutex};
|
switch (const VkResult result = master_semaphore->SubmitQueue(
|
||||||
switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
|
cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
|
||||||
case VK_SUCCESS:
|
case VK_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case VK_ERROR_DEVICE_LOST:
|
case VK_ERROR_DEVICE_LOST:
|
||||||
|
@ -265,7 +231,6 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
|
||||||
});
|
});
|
||||||
chunk->MarkSubmit();
|
chunk->MarkSubmit();
|
||||||
DispatchWork();
|
DispatchWork();
|
||||||
return signal_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::AllocateNewContext() {
|
void Scheduler::AllocateNewContext() {
|
||||||
|
|
|
@ -34,7 +34,7 @@ public:
|
||||||
~Scheduler();
|
~Scheduler();
|
||||||
|
|
||||||
/// Sends the current execution context to the GPU.
|
/// Sends the current execution context to the GPU.
|
||||||
u64 Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
||||||
|
|
||||||
/// Sends the current execution context to the GPU and waits for it to complete.
|
/// Sends the current execution context to the GPU and waits for it to complete.
|
||||||
void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
||||||
|
@ -106,8 +106,6 @@ public:
|
||||||
return *master_semaphore;
|
return *master_semaphore;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mutex submit_mutex;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Command {
|
class Command {
|
||||||
public:
|
public:
|
||||||
|
@ -203,7 +201,7 @@ private:
|
||||||
|
|
||||||
void AllocateWorkerCommandBuffer();
|
void AllocateWorkerCommandBuffer();
|
||||||
|
|
||||||
u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
||||||
|
|
||||||
void AllocateNewContext();
|
void AllocateNewContext();
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,18 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
|
||||||
return extent;
|
return extent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||||
|
if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) {
|
||||||
|
return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
} else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
|
||||||
|
return VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Unknown composite alpha flags value {:#x}",
|
||||||
|
capabilities.supportedCompositeAlpha);
|
||||||
|
return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
|
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
|
||||||
|
@ -87,16 +99,18 @@ void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device.GetLogical().WaitIdle();
|
||||||
Destroy();
|
Destroy();
|
||||||
|
|
||||||
CreateSwapchain(capabilities, srgb);
|
CreateSwapchain(capabilities, srgb);
|
||||||
CreateSemaphores();
|
CreateSemaphores();
|
||||||
|
CreateImageViews();
|
||||||
|
|
||||||
resource_ticks.clear();
|
resource_ticks.clear();
|
||||||
resource_ticks.resize(image_count);
|
resource_ticks.resize(image_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Swapchain::AcquireNextImage() {
|
void Swapchain::AcquireNextImage() {
|
||||||
const VkResult result = device.GetLogical().AcquireNextImageKHR(
|
const VkResult result = device.GetLogical().AcquireNextImageKHR(
|
||||||
*swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
|
*swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
|
||||||
VK_NULL_HANDLE, &image_index);
|
VK_NULL_HANDLE, &image_index);
|
||||||
|
@ -113,11 +127,8 @@ bool Swapchain::AcquireNextImage() {
|
||||||
LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result));
|
LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler.Wait(resource_ticks[image_index]);
|
scheduler.Wait(resource_ticks[image_index]);
|
||||||
resource_ticks[image_index] = scheduler.CurrentTick();
|
resource_ticks[image_index] = scheduler.CurrentTick();
|
||||||
|
|
||||||
return is_suboptimal || is_outdated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::Present(VkSemaphore render_semaphore) {
|
void Swapchain::Present(VkSemaphore render_semaphore) {
|
||||||
|
@ -132,7 +143,6 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
|
||||||
.pImageIndices = &image_index,
|
.pImageIndices = &image_index,
|
||||||
.pResults = nullptr,
|
.pResults = nullptr,
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{scheduler.submit_mutex};
|
|
||||||
switch (const VkResult result = present_queue.Present(present_info)) {
|
switch (const VkResult result = present_queue.Present(present_info)) {
|
||||||
case VK_SUCCESS:
|
case VK_SUCCESS:
|
||||||
break;
|
break;
|
||||||
|
@ -157,7 +167,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
||||||
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
|
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
|
||||||
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
||||||
|
|
||||||
surface_format = ChooseSwapSurfaceFormat(formats);
|
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
||||||
|
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
|
||||||
present_mode = ChooseSwapPresentMode(present_modes);
|
present_mode = ChooseSwapPresentMode(present_modes);
|
||||||
|
|
||||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||||
|
@ -182,12 +193,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
||||||
.imageColorSpace = surface_format.colorSpace,
|
.imageColorSpace = surface_format.colorSpace,
|
||||||
.imageExtent = {},
|
.imageExtent = {},
|
||||||
.imageArrayLayers = 1,
|
.imageArrayLayers = 1,
|
||||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
.preTransform = capabilities.currentTransform,
|
.preTransform = capabilities.currentTransform,
|
||||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
.compositeAlpha = alpha_flags,
|
||||||
.presentMode = present_mode,
|
.presentMode = present_mode,
|
||||||
.clipped = VK_FALSE,
|
.clipped = VK_FALSE,
|
||||||
.oldSwapchain = nullptr,
|
.oldSwapchain = nullptr,
|
||||||
|
@ -230,14 +241,45 @@ void Swapchain::CreateSemaphores() {
|
||||||
present_semaphores.resize(image_count);
|
present_semaphores.resize(image_count);
|
||||||
std::ranges::generate(present_semaphores,
|
std::ranges::generate(present_semaphores,
|
||||||
[this] { return device.GetLogical().CreateSemaphore(); });
|
[this] { return device.GetLogical().CreateSemaphore(); });
|
||||||
render_semaphores.resize(image_count);
|
}
|
||||||
std::ranges::generate(render_semaphores,
|
|
||||||
[this] { return device.GetLogical().CreateSemaphore(); });
|
void Swapchain::CreateImageViews() {
|
||||||
|
VkImageViewCreateInfo ci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.image = {},
|
||||||
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||||
|
.format = image_view_format,
|
||||||
|
.components =
|
||||||
|
{
|
||||||
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
},
|
||||||
|
.subresourceRange =
|
||||||
|
{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
image_views.resize(image_count);
|
||||||
|
for (std::size_t i = 0; i < image_count; i++) {
|
||||||
|
ci.image = images[i];
|
||||||
|
image_views[i] = device.GetLogical().CreateImageView(ci);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::Destroy() {
|
void Swapchain::Destroy() {
|
||||||
frame_index = 0;
|
frame_index = 0;
|
||||||
present_semaphores.clear();
|
present_semaphores.clear();
|
||||||
|
framebuffers.clear();
|
||||||
|
image_views.clear();
|
||||||
swapchain.reset();
|
swapchain.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
void Create(u32 width, u32 height, bool srgb);
|
void Create(u32 width, u32 height, bool srgb);
|
||||||
|
|
||||||
/// Acquires the next image in the swapchain, waits as needed.
|
/// Acquires the next image in the swapchain, waits as needed.
|
||||||
bool AcquireNextImage();
|
void AcquireNextImage();
|
||||||
|
|
||||||
/// Presents the rendered image to the swapchain.
|
/// Presents the rendered image to the swapchain.
|
||||||
void Present(VkSemaphore render_semaphore);
|
void Present(VkSemaphore render_semaphore);
|
||||||
|
@ -52,11 +52,6 @@ public:
|
||||||
return is_suboptimal;
|
return is_suboptimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true when the swapchain format is in the srgb color space
|
|
||||||
bool IsSrgb() const {
|
|
||||||
return current_srgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkExtent2D GetSize() const {
|
VkExtent2D GetSize() const {
|
||||||
return extent;
|
return extent;
|
||||||
}
|
}
|
||||||
|
@ -69,34 +64,22 @@ public:
|
||||||
return image_index;
|
return image_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t GetFrameIndex() const {
|
|
||||||
return frame_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImage GetImageIndex(std::size_t index) const {
|
VkImage GetImageIndex(std::size_t index) const {
|
||||||
return images[index];
|
return images[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImage CurrentImage() const {
|
VkImageView GetImageViewIndex(std::size_t index) const {
|
||||||
return images[image_index];
|
return *image_views[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFormat GetImageViewFormat() const {
|
VkFormat GetImageViewFormat() const {
|
||||||
return image_view_format;
|
return image_view_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFormat GetImageFormat() const {
|
|
||||||
return surface_format.format;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkSemaphore CurrentPresentSemaphore() const {
|
VkSemaphore CurrentPresentSemaphore() const {
|
||||||
return *present_semaphores[frame_index];
|
return *present_semaphores[frame_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSemaphore CurrentRenderSemaphore() const {
|
|
||||||
return *render_semaphores[frame_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetWidth() const {
|
u32 GetWidth() const {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
@ -105,10 +88,6 @@ public:
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D GetExtent() const {
|
|
||||||
return extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
|
void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
|
||||||
void CreateSemaphores();
|
void CreateSemaphores();
|
||||||
|
@ -128,9 +107,10 @@ private:
|
||||||
|
|
||||||
std::size_t image_count{};
|
std::size_t image_count{};
|
||||||
std::vector<VkImage> images;
|
std::vector<VkImage> images;
|
||||||
|
std::vector<vk::ImageView> image_views;
|
||||||
|
std::vector<vk::Framebuffer> framebuffers;
|
||||||
std::vector<u64> resource_ticks;
|
std::vector<u64> resource_ticks;
|
||||||
std::vector<vk::Semaphore> present_semaphores;
|
std::vector<vk::Semaphore> present_semaphores;
|
||||||
std::vector<vk::Semaphore> render_semaphores;
|
|
||||||
|
|
||||||
u32 width;
|
u32 width;
|
||||||
u32 height;
|
u32 height;
|
||||||
|
@ -141,7 +121,6 @@ private:
|
||||||
VkFormat image_view_format{};
|
VkFormat image_view_format{};
|
||||||
VkExtent2D extent{};
|
VkExtent2D extent{};
|
||||||
VkPresentModeKHR present_mode{};
|
VkPresentModeKHR present_mode{};
|
||||||
VkSurfaceFormatKHR surface_format{};
|
|
||||||
|
|
||||||
bool current_srgb{};
|
bool current_srgb{};
|
||||||
bool current_fps_unlocked{};
|
bool current_fps_unlocked{};
|
||||||
|
|
|
@ -14,18 +14,13 @@ namespace Vulkan {
|
||||||
|
|
||||||
UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_)
|
UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_)
|
||||||
: device{device_}, scheduler{scheduler_} {
|
: device{device_}, scheduler{scheduler_} {
|
||||||
payload_start = payload.data();
|
|
||||||
payload_cursor = payload.data();
|
payload_cursor = payload.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateDescriptorQueue::~UpdateDescriptorQueue() = default;
|
UpdateDescriptorQueue::~UpdateDescriptorQueue() = default;
|
||||||
|
|
||||||
void UpdateDescriptorQueue::TickFrame() {
|
void UpdateDescriptorQueue::TickFrame() {
|
||||||
if (++frame_index >= FRAMES_IN_FLIGHT) {
|
payload_cursor = payload.data();
|
||||||
frame_index = 0;
|
|
||||||
}
|
|
||||||
payload_start = payload.data() + frame_index * FRAME_PAYLOAD_SIZE;
|
|
||||||
payload_cursor = payload_start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateDescriptorQueue::Acquire() {
|
void UpdateDescriptorQueue::Acquire() {
|
||||||
|
@ -33,10 +28,10 @@ void UpdateDescriptorQueue::Acquire() {
|
||||||
// This is the maximum number of entries a single draw call might use.
|
// This is the maximum number of entries a single draw call might use.
|
||||||
static constexpr size_t MIN_ENTRIES = 0x400;
|
static constexpr size_t MIN_ENTRIES = 0x400;
|
||||||
|
|
||||||
if (std::distance(payload_start, payload_cursor) + MIN_ENTRIES >= FRAME_PAYLOAD_SIZE) {
|
if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) {
|
||||||
LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
|
LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
|
||||||
scheduler.WaitWorker();
|
scheduler.WaitWorker();
|
||||||
payload_cursor = payload_start;
|
payload_cursor = payload.data();
|
||||||
}
|
}
|
||||||
upload_start = payload_cursor;
|
upload_start = payload_cursor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,6 @@ struct DescriptorUpdateEntry {
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateDescriptorQueue final {
|
class UpdateDescriptorQueue final {
|
||||||
// This should be plenty for the vast majority of cases. Most desktop platforms only
|
|
||||||
// provide up to 3 swapchain images.
|
|
||||||
static constexpr size_t FRAMES_IN_FLIGHT = 5;
|
|
||||||
static constexpr size_t FRAME_PAYLOAD_SIZE = 0x10000;
|
|
||||||
static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_);
|
explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_);
|
||||||
~UpdateDescriptorQueue();
|
~UpdateDescriptorQueue();
|
||||||
|
@ -79,11 +73,9 @@ private:
|
||||||
const Device& device;
|
const Device& device;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
|
||||||
size_t frame_index{0};
|
|
||||||
DescriptorUpdateEntry* payload_cursor = nullptr;
|
DescriptorUpdateEntry* payload_cursor = nullptr;
|
||||||
DescriptorUpdateEntry* payload_start = nullptr;
|
|
||||||
const DescriptorUpdateEntry* upload_start = nullptr;
|
const DescriptorUpdateEntry* upload_start = nullptr;
|
||||||
std::array<DescriptorUpdateEntry, PAYLOAD_SIZE> payload;
|
std::array<DescriptorUpdateEntry, 0x10000> payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -145,7 +145,6 @@
|
||||||
FEATURE_NAME(robustness2, robustImageAccess2) \
|
FEATURE_NAME(robustness2, robustImageAccess2) \
|
||||||
FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \
|
FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \
|
||||||
FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \
|
FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \
|
||||||
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
|
|
||||||
FEATURE_NAME(variable_pointer, variablePointers) \
|
FEATURE_NAME(variable_pointer, variablePointers) \
|
||||||
FEATURE_NAME(variable_pointer, variablePointersStorageBuffer)
|
FEATURE_NAME(variable_pointer, variablePointersStorageBuffer)
|
||||||
|
|
||||||
|
@ -158,6 +157,7 @@
|
||||||
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
|
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
|
||||||
FEATURE_NAME(shader_float16_int8, shaderFloat16) \
|
FEATURE_NAME(shader_float16_int8, shaderFloat16) \
|
||||||
FEATURE_NAME(shader_float16_int8, shaderInt8) \
|
FEATURE_NAME(shader_float16_int8, shaderInt8) \
|
||||||
|
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
|
||||||
FEATURE_NAME(transform_feedback, transformFeedback) \
|
FEATURE_NAME(transform_feedback, transformFeedback) \
|
||||||
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
|
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
|
||||||
FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
|
FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
|
||||||
|
@ -493,6 +493,10 @@ public:
|
||||||
return extensions.shader_atomic_int64;
|
return extensions.shader_atomic_int64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasTimelineSemaphore() const {
|
||||||
|
return features.timeline_semaphore.timelineSemaphore;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the minimum supported version of SPIR-V.
|
/// Returns the minimum supported version of SPIR-V.
|
||||||
u32 SupportedSpirvVersion() const {
|
u32 SupportedSpirvVersion() const {
|
||||||
if (instance_version >= VK_API_VERSION_1_3) {
|
if (instance_version >= VK_API_VERSION_1_3) {
|
||||||
|
|
Loading…
Reference in a new issue