Compare commits
45 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36d3df82f9 | ||
|
|
7f5c643852 | ||
|
|
65bd03d74c | ||
|
|
7f3c2525e6 | ||
|
|
fd72d889bf | ||
|
|
cb1e63ef09 | ||
|
|
c8b3f09876 | ||
|
|
d08b876c9d | ||
|
|
b4a6ce02ce | ||
|
|
9333ee29ca | ||
|
|
2a16fd7ffc | ||
|
|
4b4f883aef | ||
|
|
2c6efda235 | ||
|
|
7474382266 | ||
|
|
a264fac943 | ||
|
|
72b1fae984 | ||
|
|
4e6c64bf8d | ||
|
|
4a6eff3b7b | ||
|
|
e9610ec0dd | ||
|
|
eaee73f95d | ||
|
|
c82bccab56 | ||
|
|
1916213311 | ||
|
|
8ea6261547 | ||
|
|
93fca5d9cf | ||
|
|
c21a6c6b37 | ||
|
|
48cd61d9c8 | ||
|
|
ddc242dd51 | ||
|
|
8d4bb10d44 | ||
|
|
84c6134264 | ||
|
|
4f78f5c0df | ||
|
|
1d36aec267 | ||
|
|
9de523fd90 | ||
|
|
d347623d6f | ||
|
|
40db288a2a | ||
|
|
ad2f47b579 | ||
|
|
fd1ef25257 | ||
|
|
41e99d8880 | ||
|
|
359db6a673 | ||
|
|
0590dd2971 | ||
|
|
d3b9599b2d | ||
|
|
d34d0bfc87 | ||
|
|
6c8b788d32 | ||
|
|
9249fadb9e | ||
|
|
cb8e4a4633 | ||
|
|
9ae972ab4e |
@@ -34,6 +34,7 @@
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -558,7 +559,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::RandomEntropy:
|
||||
*result = 0;
|
||||
*result = Settings::values.rng_seed.value_or(0);
|
||||
break;
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = vm_manager.GetASLRRegionBaseAddress();
|
||||
|
||||
@@ -338,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
|
||||
return on_new_message;
|
||||
}
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
|
||||
return on_operation_mode_changed;
|
||||
}
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
messages.push(msg);
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||
@@ -388,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event);
|
||||
rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(15);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
@@ -414,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event);
|
||||
rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
|
||||
@@ -444,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -454,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
|
||||
: APM::PerformanceMode::Handheld));
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
@@ -840,8 +883,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(
|
||||
AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<IdleSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<OMM>()->InstallAsService(service_manager);
|
||||
std::make_shared<SPSM>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -39,6 +40,31 @@ enum SystemLanguage {
|
||||
TraditionalChinese = 16,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
FocusStateChanged = 15,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
};
|
||||
|
||||
AppletMessageQueue();
|
||||
~AppletMessageQueue();
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
|
||||
void PushMessage(AppletMessage msg);
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
void OperationModeChanged();
|
||||
|
||||
private:
|
||||
std::queue<AppletMessage> messages;
|
||||
Kernel::SharedPtr<Kernel::Event> on_new_message;
|
||||
Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
public:
|
||||
IWindowController();
|
||||
@@ -102,7 +128,7 @@ private:
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
public:
|
||||
ICommonStateGetter();
|
||||
explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
@@ -126,6 +152,7 @@ private:
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
|
||||
@@ -12,8 +12,10 @@ namespace Service::AM {
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -32,7 +34,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -93,12 +95,15 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -119,7 +124,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -186,31 +191,34 @@ private:
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
@@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
|
||||
AppletAE::~AppletAE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,15 +17,19 @@ namespace AM {
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~AppletAE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -12,8 +12,10 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
@@ -70,7 +72,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -89,17 +91,20 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
@@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
|
||||
AppletOE::~AppletOE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,13 +17,17 @@ namespace AM {
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~AppletOE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -40,6 +40,29 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id) {
|
||||
switch (npad_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return npad_id;
|
||||
case 8:
|
||||
case NPAD_HANDHELD:
|
||||
return 8;
|
||||
case 9:
|
||||
case NPAD_UNKNOWN:
|
||||
return 9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
@@ -288,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
switch (controller_type) {
|
||||
case NPadControllerType::Handheld:
|
||||
handheld_entry.connection_status.raw = 0;
|
||||
handheld_entry.connection_status.IsConnected.Assign(1);
|
||||
if (!Settings::values.use_docked_mode) {
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
}
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
handheld_entry.l_stick = lstick_entry;
|
||||
handheld_entry.r_stick = rstick_entry;
|
||||
@@ -370,16 +394,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
std::memcpy(supported_npad_id_types.data(), data, length);
|
||||
bool had_controller_update = false;
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
|
||||
auto& controller = connected_controllers[i];
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
controller.type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
InitNewlyAddedControler(i);
|
||||
const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
const bool is_handheld = (best_type == NPadControllerType::Handheld ||
|
||||
PREFERRED_CONTROLLER == NPadControllerType::Handheld);
|
||||
if (is_handheld) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
AddNewController(best_type);
|
||||
} else {
|
||||
controller.type = best_type;
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
@@ -457,15 +495,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = true;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = false;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
|
||||
}
|
||||
|
||||
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
|
||||
@@ -3,18 +3,23 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
#include "core/hle/service/spl/module.h"
|
||||
#include "core/hle/service/spl/spl.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -24,7 +29,7 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
std::size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::vector<u8> data(size);
|
||||
std::generate(data.begin(), data.end(), std::rand);
|
||||
std::generate(data.begin(), data.end(), rng);
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::SPL {
|
||||
@@ -19,6 +20,9 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
private:
|
||||
std::mt19937 rng;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
|
||||
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
|
||||
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
|
||||
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
|
||||
{400, nullptr, "GetClockSnapshot"},
|
||||
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
|
||||
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
|
||||
{500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{501, nullptr, "CalculateSpanBetween"},
|
||||
|
||||
@@ -15,6 +15,44 @@
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& additional_info,
|
||||
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
|
||||
const std::time_t time(posix_time);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
calendar_time = {};
|
||||
additional_info = {};
|
||||
return;
|
||||
}
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
|
||||
additional_info.day_of_week = tm->tm_wday;
|
||||
additional_info.day_of_year = tm->tm_yday;
|
||||
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
|
||||
additional_info.utc_offset = 0;
|
||||
}
|
||||
|
||||
static u64 CalendarToPosix(const CalendarTime& calendar_time,
|
||||
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
|
||||
std::tm time{};
|
||||
time.tm_year = calendar_time.year - 1900;
|
||||
time.tm_mon = calendar_time.month - 1;
|
||||
time.tm_mday = calendar_time.day;
|
||||
|
||||
time.tm_hour = calendar_time.hour;
|
||||
time.tm_min = calendar_time.minute;
|
||||
time.tm_sec = calendar_time.second;
|
||||
|
||||
std::time_t epoch_time = std::mktime(&time);
|
||||
return static_cast<u64>(epoch_time);
|
||||
}
|
||||
|
||||
class ISystemClock final : public ServiceFramework<ISystemClock> {
|
||||
public:
|
||||
ISystemClock() : ServiceFramework("ISystemClock") {
|
||||
@@ -80,8 +118,8 @@ public:
|
||||
{5, nullptr, "GetTimeZoneRuleVersion"},
|
||||
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{201, nullptr, "ToPosixTime"},
|
||||
{202, nullptr, "ToPosixTimeWithMyRule"},
|
||||
{201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
|
||||
{202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -151,24 +189,29 @@ private:
|
||||
rb.PushRaw(additional_info);
|
||||
}
|
||||
|
||||
void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) {
|
||||
std::time_t t(posix_time);
|
||||
std::tm* tm = std::localtime(&t);
|
||||
if (!tm) {
|
||||
return;
|
||||
}
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
void ToPosixTime(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(ogniK): Figure out how to handle multiple times
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar_time = rp.PopRaw<CalendarTime>();
|
||||
auto posix_time = CalendarToPosix(calendar_time, {});
|
||||
|
||||
additional_info.day_of_week = tm->tm_wday;
|
||||
additional_info.day_of_year = tm->tm_yday;
|
||||
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
|
||||
additional_info.utc_offset = 0;
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u32>(1); // Amount of times we're returning
|
||||
ctx.WriteBuffer(&posix_time, sizeof(u64));
|
||||
}
|
||||
|
||||
void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar_time = rp.PopRaw<CalendarTime>();
|
||||
auto posix_time = CalendarToPosix(calendar_time, {});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u32>(1); // Amount of times we're returning
|
||||
ctx.WriteBuffer(&posix_time, sizeof(u64));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -207,6 +250,55 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto unknown_u8 = rp.PopRaw<u8>();
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
|
||||
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
CalendarTime calendar_time{};
|
||||
const std::time_t time(time_since_epoch);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
|
||||
return;
|
||||
}
|
||||
SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
|
||||
1000};
|
||||
|
||||
LocationName location_name{"UTC"};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
clock_snapshot.system_posix_time = time_since_epoch;
|
||||
clock_snapshot.network_posix_time = time_since_epoch;
|
||||
clock_snapshot.system_calendar_time = calendar_time;
|
||||
clock_snapshot.network_calendar_time = calendar_time;
|
||||
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
|
||||
|
||||
clock_snapshot.system_calendar_info = additional_info;
|
||||
clock_snapshot.network_calendar_info = additional_info;
|
||||
|
||||
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
|
||||
clock_snapshot.location_name = location_name;
|
||||
clock_snapshot.clock_auto_adjustment_enabled = 1;
|
||||
clock_snapshot.ipc_u8 = unknown_u8;
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
|
||||
: ServiceFramework(name), time(std::move(time)) {}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Time {
|
||||
@@ -53,6 +54,23 @@ struct SystemClockContext {
|
||||
static_assert(sizeof(SystemClockContext) == 0x20,
|
||||
"SystemClockContext structure has incorrect size");
|
||||
|
||||
struct ClockSnapshot {
|
||||
SystemClockContext user_clock_context;
|
||||
SystemClockContext network_clock_context;
|
||||
s64_le system_posix_time;
|
||||
s64_le network_posix_time;
|
||||
CalendarTime system_calendar_time;
|
||||
CalendarTime network_calendar_time;
|
||||
CalendarAdditionalInfo system_calendar_info;
|
||||
CalendarAdditionalInfo network_calendar_info;
|
||||
SteadyClockTimePoint steady_clock_timepoint;
|
||||
LocationName location_name;
|
||||
u8 clock_auto_adjustment_enabled;
|
||||
u8 ipc_u8;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
@@ -65,6 +83,7 @@ public:
|
||||
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
|
||||
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
|
||||
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
|
||||
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> time;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -114,8 +115,9 @@ struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
bool enable_nfc;
|
||||
int current_user;
|
||||
int language_index;
|
||||
std::optional<u64> rng_seed;
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
|
||||
// Controls
|
||||
std::array<std::string, NativeButton::NumButtons> buttons;
|
||||
|
||||
@@ -21,6 +21,7 @@ add_library(video_core STATIC
|
||||
macro_interpreter.h
|
||||
memory_manager.cpp
|
||||
memory_manager.h
|
||||
rasterizer_cache.cpp
|
||||
rasterizer_cache.h
|
||||
rasterizer_interface.h
|
||||
renderer_base.cpp
|
||||
|
||||
@@ -53,6 +53,19 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
}
|
||||
regs.stencil_front_op_fail = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_front_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_front_mask = 0xFFFFFFFF;
|
||||
regs.stencil_two_side_enable = 1;
|
||||
regs.stencil_back_op_fail = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_back_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
@@ -108,8 +121,16 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
|
||||
}
|
||||
|
||||
u32 old = regs.reg_array[method];
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
if (value != old) {
|
||||
if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
|
||||
method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
|
||||
dirty_flags.vertex_attrib_format = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(macros.data): {
|
||||
ProcessMacroUpload(value);
|
||||
|
||||
@@ -345,6 +345,14 @@ public:
|
||||
Invert = 6,
|
||||
IncrWrap = 7,
|
||||
DecrWrap = 8,
|
||||
KeepOGL = 0x1E00,
|
||||
ZeroOGL = 0,
|
||||
ReplaceOGL = 0x1E01,
|
||||
IncrOGL = 0x1E02,
|
||||
DecrOGL = 0x1E03,
|
||||
InvertOGL = 0x150A,
|
||||
IncrWrapOGL = 0x8507,
|
||||
DecrWrapOGL = 0x8508,
|
||||
};
|
||||
|
||||
enum class MemoryLayout : u32 {
|
||||
@@ -1004,6 +1012,12 @@ public:
|
||||
State state{};
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
struct DirtyFlags {
|
||||
bool vertex_attrib_format = true;
|
||||
};
|
||||
|
||||
DirtyFlags dirty_flags;
|
||||
|
||||
/// Reads a register value located at the input method address
|
||||
u32 GetRegisterValue(u32 method) const;
|
||||
|
||||
|
||||
7
src/video_core/rasterizer_cache.cpp
Normal file
7
src/video_core/rasterizer_cache.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
|
||||
RasterizerCacheObject::~RasterizerCacheObject() = default;
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
class RasterizerCacheObject {
|
||||
public:
|
||||
virtual ~RasterizerCacheObject();
|
||||
|
||||
/// Gets the address of the shader in guest memory, required for cache management
|
||||
virtual VAddr GetAddr() const = 0;
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
using PixelFormat = VideoCore::Surface::PixelFormat;
|
||||
using SurfaceType = VideoCore::Surface::SurfaceType;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
|
||||
@@ -123,18 +124,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() {}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexArrays() {
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
void RasterizerOpenGL::SetupVertexFormat() {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
if (!gpu.dirty_flags.vertex_attrib_format)
|
||||
return;
|
||||
gpu.dirty_flags.vertex_attrib_format = false;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
|
||||
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
|
||||
auto& VAO = iter->second;
|
||||
|
||||
if (is_cache_miss) {
|
||||
VAO.Create();
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.Apply();
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
|
||||
// around.
|
||||
@@ -176,8 +182,13 @@ void RasterizerOpenGL::SetupVertexArrays() {
|
||||
}
|
||||
}
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.Apply();
|
||||
state.ApplyVertexBufferState();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
MICROPROFILE_SCOPE(OpenGL_VB);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
// Upload all guest vertex arrays sequentially to our buffer
|
||||
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
@@ -204,6 +215,9 @@ void RasterizerOpenGL::SetupVertexArrays() {
|
||||
glVertexBindingDivisor(index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
}
|
||||
|
||||
DrawParameters RasterizerOpenGL::SetupDraw() {
|
||||
@@ -328,8 +342,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
|
||||
@@ -398,8 +410,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
||||
cached_pages.add({pages_interval, delta});
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb,
|
||||
bool preserve_contents,
|
||||
void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
|
||||
bool using_depth_fb, bool preserve_contents,
|
||||
std::optional<std::size_t> single_color_target) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
@@ -415,9 +427,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
|
||||
ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
state.draw.draw_framebuffer = framebuffer.handle;
|
||||
state.Apply();
|
||||
state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
|
||||
current_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
current_state.ApplyFramebufferState();
|
||||
current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
|
||||
|
||||
if (using_color_fb) {
|
||||
if (single_color_target) {
|
||||
@@ -495,10 +507,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
}
|
||||
|
||||
SyncViewport();
|
||||
|
||||
state.Apply();
|
||||
SyncViewport(current_state);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Clear() {
|
||||
@@ -511,22 +520,23 @@ void RasterizerOpenGL::Clear() {
|
||||
bool use_stencil{};
|
||||
|
||||
OpenGLState clear_state;
|
||||
clear_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
|
||||
|
||||
if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
|
||||
regs.clear_buffers.A) {
|
||||
use_color = true;
|
||||
}
|
||||
if (use_color) {
|
||||
clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
|
||||
clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
if (regs.clear_buffers.Z) {
|
||||
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
|
||||
use_depth = true;
|
||||
|
||||
// Always enable the depth write when clearing the depth buffer. The depth write mask is
|
||||
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
|
||||
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
|
||||
// true.
|
||||
clear_state.depth.test_enabled = true;
|
||||
clear_state.depth.test_func = GL_ALWAYS;
|
||||
}
|
||||
@@ -543,11 +553,8 @@ void RasterizerOpenGL::Clear() {
|
||||
|
||||
ScopeAcquireGLContext acquire_context{emu_window};
|
||||
|
||||
ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
|
||||
ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
|
||||
regs.clear_buffers.RT.Value());
|
||||
// Copy the sRGB setting to the clear state to avoid problem with
|
||||
// specific driver implementations
|
||||
clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
|
||||
clear_state.Apply();
|
||||
|
||||
if (use_color) {
|
||||
@@ -573,7 +580,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
|
||||
ScopeAcquireGLContext acquire_context{emu_window};
|
||||
|
||||
ConfigureFramebuffers();
|
||||
ConfigureFramebuffers(state);
|
||||
SyncColorMask();
|
||||
SyncDepthTestState();
|
||||
SyncStencilTestState();
|
||||
@@ -594,7 +601,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.Apply();
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
std::size_t buffer_size = CalculateVertexArraysSize();
|
||||
|
||||
@@ -621,7 +628,8 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
|
||||
buffer_cache.Map(buffer_size);
|
||||
|
||||
SetupVertexArrays();
|
||||
SetupVertexFormat();
|
||||
SetupVertexBuffer();
|
||||
DrawParameters params = SetupDraw();
|
||||
SetupShaders(params.primitive_mode);
|
||||
|
||||
@@ -725,9 +733,9 @@ void RasterizerOpenGL::SamplerInfo::Create() {
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
|
||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
|
||||
const GLuint s = sampler.handle;
|
||||
|
||||
const Tegra::Texture::TSCEntry& config = info.tsc;
|
||||
if (mag_filter != config.mag_filter) {
|
||||
mag_filter = config.mag_filter;
|
||||
glSamplerParameteri(
|
||||
@@ -778,6 +786,22 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
|
||||
}
|
||||
}
|
||||
if (info.tic.use_header_opt_control == 0) {
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
|
||||
static_cast<float>(1 << info.tic.max_anisotropy.Value()));
|
||||
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
static_cast<float>(1 << info.tic.max_anisotropy.Value()));
|
||||
}
|
||||
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
|
||||
static_cast<float>(info.tic.res_min_mip_level.Value()));
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
|
||||
static_cast<float>(info.tic.res_max_mip_level.Value() == 0
|
||||
? 16
|
||||
: info.tic.res_max_mip_level.Value()));
|
||||
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
|
||||
}
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
|
||||
@@ -875,7 +899,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
continue;
|
||||
}
|
||||
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture);
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
|
||||
@@ -897,15 +921,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
return current_unit + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncViewport() {
|
||||
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
|
||||
auto& viewport = state.viewports[i];
|
||||
auto& viewport = current_state.viewports[i];
|
||||
viewport.x = viewport_rect.left;
|
||||
viewport.y = viewport_rect.bottom;
|
||||
viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
|
||||
viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
|
||||
viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
|
||||
viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
|
||||
viewport.depth_range_far = regs.viewport[i].depth_range_far;
|
||||
viewport.depth_range_near = regs.viewport[i].depth_range_near;
|
||||
}
|
||||
@@ -970,9 +994,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(bunnei): Verify behavior when this is not set
|
||||
ASSERT(regs.stencil_two_side_enable);
|
||||
|
||||
state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
|
||||
state.stencil.front.test_ref = regs.stencil_front_func_ref;
|
||||
state.stencil.front.test_mask = regs.stencil_front_func_mask;
|
||||
@@ -980,14 +1001,23 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
|
||||
state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
|
||||
state.stencil.front.write_mask = regs.stencil_front_mask;
|
||||
|
||||
state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
|
||||
state.stencil.back.test_ref = regs.stencil_back_func_ref;
|
||||
state.stencil.back.test_mask = regs.stencil_back_func_mask;
|
||||
state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
|
||||
state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
|
||||
state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
|
||||
state.stencil.back.write_mask = regs.stencil_back_mask;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
|
||||
state.stencil.back.test_ref = regs.stencil_back_func_ref;
|
||||
state.stencil.back.test_mask = regs.stencil_back_func_mask;
|
||||
state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
|
||||
state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
|
||||
state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
|
||||
state.stencil.back.write_mask = regs.stencil_back_mask;
|
||||
} else {
|
||||
state.stencil.back.test_func = GL_ALWAYS;
|
||||
state.stencil.back.test_ref = 0;
|
||||
state.stencil.back.test_mask = 0xFFFFFFFF;
|
||||
state.stencil.back.write_mask = 0xFFFFFFFF;
|
||||
state.stencil.back.action_stencil_fail = GL_KEEP;
|
||||
state.stencil.back.action_depth_fail = GL_KEEP;
|
||||
state.stencil.back.action_depth_pass = GL_KEEP;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncColorMask() {
|
||||
@@ -1099,9 +1129,8 @@ void RasterizerOpenGL::CheckAlphaTests() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
|
||||
LOG_CRITICAL(
|
||||
Render_OpenGL,
|
||||
"Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
|
||||
LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
|
||||
"this behavior is undefined.");
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
/// SamplerInfo struct.
|
||||
void Create();
|
||||
/// Syncs the sampler object with the config, updating any necessary state.
|
||||
void SyncWithConfig(const Tegra::Texture::TSCEntry& config);
|
||||
void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
|
||||
|
||||
private:
|
||||
Tegra::Texture::TextureFilter mag_filter;
|
||||
@@ -109,8 +109,8 @@ private:
|
||||
* @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
|
||||
* @param single_color_target Specifies if a single color buffer target should be used.
|
||||
*/
|
||||
void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true,
|
||||
bool preserve_contents = true,
|
||||
void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
|
||||
bool using_depth_fb = true, bool preserve_contents = true,
|
||||
std::optional<std::size_t> single_color_target = {});
|
||||
|
||||
/*
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
GLenum primitive_mode, u32 current_unit);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport();
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
|
||||
/// Syncs the clip enabled status to match the guest state
|
||||
void SyncClipEnabled();
|
||||
@@ -207,7 +207,8 @@ private:
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
void SetupVertexArrays();
|
||||
void SetupVertexFormat();
|
||||
void SetupVertexBuffer();
|
||||
|
||||
DrawParameters SetupDraw();
|
||||
|
||||
|
||||
@@ -314,6 +314,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
|
||||
|
||||
// Depth formats
|
||||
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
|
||||
@@ -456,6 +458,8 @@ static constexpr GLConversionArray morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
|
||||
MortonCopy<true, PixelFormat::Z32F>,
|
||||
MortonCopy<true, PixelFormat::Z16>,
|
||||
MortonCopy<true, PixelFormat::Z24S8>,
|
||||
@@ -526,6 +530,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::Z32F>,
|
||||
MortonCopy<false, PixelFormat::Z16>,
|
||||
MortonCopy<false, PixelFormat::Z24S8>,
|
||||
@@ -580,7 +586,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
state.draw.draw_framebuffer = draw_fb_handle;
|
||||
// Set sRGB enabled if the destination surfaces need it
|
||||
state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
|
||||
state.Apply();
|
||||
state.ApplyFramebufferState();
|
||||
|
||||
u32 buffers{};
|
||||
|
||||
@@ -932,7 +938,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_5X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB: {
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X8:
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB: {
|
||||
// Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
|
||||
u32 block_width{};
|
||||
u32 block_height{};
|
||||
@@ -967,7 +975,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
case PixelFormat::ASTC_2D_8X8:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB: {
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_5X5:
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X8:
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB: {
|
||||
LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
|
||||
static_cast<u32>(pixel_format));
|
||||
UNREACHABLE();
|
||||
@@ -1336,6 +1348,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
break;
|
||||
|
||||
@@ -121,12 +121,16 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
|
||||
}
|
||||
|
||||
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
|
||||
const std::string& glsl_topology,
|
||||
const std::string& glsl_topology, u32 max_vertices,
|
||||
const std::string& debug_name) {
|
||||
if (target_program.handle != 0) {
|
||||
return target_program.handle;
|
||||
}
|
||||
const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
|
||||
std::string source = "#version 430 core\n";
|
||||
source += "layout (" + glsl_topology + ") in;\n";
|
||||
source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n';
|
||||
source += geometry_programs.code;
|
||||
|
||||
OGLShader shader;
|
||||
shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
|
||||
target_program.Create(true, shader.handle);
|
||||
|
||||
@@ -48,22 +48,23 @@ public:
|
||||
}
|
||||
switch (primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
|
||||
return LazyGeometryProgram(geometry_programs.lines, "lines", 2, "ShaderLines");
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
|
||||
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 4,
|
||||
"ShaderLinesAdjacency");
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
|
||||
return LazyGeometryProgram(geometry_programs.triangles, "triangles", 3,
|
||||
"ShaderTriangles");
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
|
||||
"ShaderLines");
|
||||
6, "ShaderTrianglesAdjacency");
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
}
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
private:
|
||||
/// Generates a geometry shader or returns one that already exists.
|
||||
GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
|
||||
const std::string& debug_name);
|
||||
u32 max_vertices, const std::string& debug_name);
|
||||
|
||||
VAddr addr;
|
||||
Maxwell::ShaderProgram program_type;
|
||||
|
||||
@@ -494,10 +494,10 @@ public:
|
||||
// instruction for now.
|
||||
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
|
||||
// TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
|
||||
// shader. These instructions use a dirty register as buffer index. To avoid some
|
||||
// drivers from complaining for the out of boundary writes, guard them.
|
||||
const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
|
||||
std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
|
||||
// shader. These instructions use a dirty register as buffer index, to avoid some
|
||||
// drivers from complaining about out of boundary writes, guard them.
|
||||
const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
|
||||
std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
|
||||
shader.AddLine("amem[" + buf_index + "][" +
|
||||
std::to_string(static_cast<u32>(attribute)) + ']' +
|
||||
GetSwizzle(elem) + " = " + src + ';');
|
||||
@@ -811,7 +811,11 @@ private:
|
||||
std::optional<Register> vertex = {}) {
|
||||
auto GeometryPass = [&](const std::string& name) {
|
||||
if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
|
||||
return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']';
|
||||
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games set
|
||||
// an 0x80000000 index for those and the shader fails to build. Find out why this
|
||||
// happens and what's its intent.
|
||||
return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) +
|
||||
" % MAX_VERTEX_INPUT]";
|
||||
}
|
||||
return name;
|
||||
};
|
||||
@@ -2742,12 +2746,12 @@ private:
|
||||
}
|
||||
case 3: {
|
||||
if (is_array) {
|
||||
UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented");
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
texture_type = Tegra::Shader::TextureType::Texture2D;
|
||||
is_array = false;
|
||||
const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
|
||||
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord =
|
||||
"vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");";
|
||||
} else {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
@@ -2777,7 +2781,11 @@ private:
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LZ: {
|
||||
texture = "textureLod(" + sampler + ", coords, 0.0)";
|
||||
if (depth_compare && is_array) {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
} else {
|
||||
texture = "textureLod(" + sampler + ", coords, 0.0)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LL: {
|
||||
|
||||
@@ -82,8 +82,8 @@ void main() {
|
||||
}
|
||||
|
||||
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
|
||||
std::string out = "#version 430 core\n";
|
||||
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
// Version is intentionally skipped in shader generation, it's added by the lazy compilation.
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += Decompiler::GetCommonDeclarations();
|
||||
out += "bool exec_geometry();\n";
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ void OpenGLState::ApplySamplers() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::Apply() const {
|
||||
void OpenGLState::ApplyFramebufferState() const {
|
||||
// Framebuffer
|
||||
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
|
||||
@@ -435,7 +435,9 @@ void OpenGLState::Apply() const {
|
||||
if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyVertexBufferState() const {
|
||||
// Vertex array
|
||||
if (draw.vertex_array != cur_state.draw.vertex_array) {
|
||||
glBindVertexArray(draw.vertex_array);
|
||||
@@ -445,7 +447,11 @@ void OpenGLState::Apply() const {
|
||||
if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::Apply() const {
|
||||
ApplyFramebufferState();
|
||||
ApplyVertexBufferState();
|
||||
// Uniform buffer
|
||||
if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
|
||||
|
||||
@@ -181,6 +181,10 @@ public:
|
||||
}
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply() const;
|
||||
/// Apply only the state afecting the framebuffer
|
||||
void ApplyFramebufferState() const;
|
||||
/// Apply only the state afecting the vertex buffer
|
||||
void ApplyVertexBufferState() const;
|
||||
/// Set the initial OpenGL state
|
||||
static void ApplyDefaultState();
|
||||
/// Resets any references to the given resource
|
||||
|
||||
@@ -159,10 +159,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}",
|
||||
static_cast<u32>(filter_mode));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
|
||||
return GL_LINEAR;
|
||||
}
|
||||
|
||||
inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
@@ -183,9 +181,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
|
||||
return GL_REPEAT;
|
||||
}
|
||||
|
||||
inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
@@ -207,10 +204,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
case Tegra::Texture::DepthCompareFunc::Always:
|
||||
return GL_ALWAYS;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}",
|
||||
static_cast<u32>(func));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
|
||||
static_cast<u32>(func));
|
||||
return GL_GREATER;
|
||||
}
|
||||
|
||||
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
@@ -226,9 +222,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
return GL_MAX;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
|
||||
return GL_FUNC_ADD;
|
||||
}
|
||||
|
||||
inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
|
||||
@@ -291,9 +286,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
|
||||
return GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
|
||||
return GL_ZERO;
|
||||
}
|
||||
|
||||
inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
|
||||
@@ -312,9 +306,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
|
||||
case Tegra::Texture::SwizzleSource::OneFloat:
|
||||
return GL_ONE;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
|
||||
return GL_ZERO;
|
||||
}
|
||||
|
||||
inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
|
||||
@@ -344,33 +337,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
|
||||
case Maxwell::ComparisonOp::AlwaysOld:
|
||||
return GL_ALWAYS;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
|
||||
return GL_ALWAYS;
|
||||
}
|
||||
|
||||
inline GLenum StencilOp(Maxwell::StencilOp stencil) {
|
||||
switch (stencil) {
|
||||
case Maxwell::StencilOp::Keep:
|
||||
case Maxwell::StencilOp::KeepOGL:
|
||||
return GL_KEEP;
|
||||
case Maxwell::StencilOp::Zero:
|
||||
case Maxwell::StencilOp::ZeroOGL:
|
||||
return GL_ZERO;
|
||||
case Maxwell::StencilOp::Replace:
|
||||
case Maxwell::StencilOp::ReplaceOGL:
|
||||
return GL_REPLACE;
|
||||
case Maxwell::StencilOp::Incr:
|
||||
case Maxwell::StencilOp::IncrOGL:
|
||||
return GL_INCR;
|
||||
case Maxwell::StencilOp::Decr:
|
||||
case Maxwell::StencilOp::DecrOGL:
|
||||
return GL_DECR;
|
||||
case Maxwell::StencilOp::Invert:
|
||||
case Maxwell::StencilOp::InvertOGL:
|
||||
return GL_INVERT;
|
||||
case Maxwell::StencilOp::IncrWrap:
|
||||
case Maxwell::StencilOp::IncrWrapOGL:
|
||||
return GL_INCR_WRAP;
|
||||
case Maxwell::StencilOp::DecrWrap:
|
||||
case Maxwell::StencilOp::DecrWrapOGL:
|
||||
return GL_DECR_WRAP;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
|
||||
return GL_KEEP;
|
||||
}
|
||||
|
||||
inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
|
||||
@@ -380,9 +379,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
|
||||
case Maxwell::Cull::FrontFace::CounterClockWise:
|
||||
return GL_CCW;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
|
||||
return GL_CCW;
|
||||
}
|
||||
|
||||
inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
|
||||
@@ -394,9 +392,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
|
||||
case Maxwell::Cull::CullFace::FrontAndBack:
|
||||
return GL_FRONT_AND_BACK;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
|
||||
return GL_BACK;
|
||||
}
|
||||
|
||||
inline GLenum LogicOp(Maxwell::LogicOperation operation) {
|
||||
@@ -434,9 +431,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
|
||||
case Maxwell::LogicOperation::Set:
|
||||
return GL_SET;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
|
||||
return GL_COPY;
|
||||
}
|
||||
|
||||
} // namespace MaxwellToGL
|
||||
|
||||
@@ -306,6 +306,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
|
||||
case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
|
||||
return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
|
||||
case Tegra::Texture::TextureFormat::ASTC_2D_10X8:
|
||||
return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8;
|
||||
case Tegra::Texture::TextureFormat::R16_G16:
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::FLOAT:
|
||||
@@ -453,6 +455,8 @@ bool IsPixelFormatASTC(PixelFormat format) {
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X8:
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -74,19 +74,21 @@ enum class PixelFormat {
|
||||
ASTC_2D_5X4_SRGB = 56,
|
||||
ASTC_2D_5X5 = 57,
|
||||
ASTC_2D_5X5_SRGB = 58,
|
||||
ASTC_2D_10X8 = 59,
|
||||
ASTC_2D_10X8_SRGB = 60,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// Depth formats
|
||||
Z32F = 59,
|
||||
Z16 = 60,
|
||||
Z32F = 61,
|
||||
Z16 = 62,
|
||||
|
||||
MaxDepthFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 61,
|
||||
S8Z24 = 62,
|
||||
Z32FS8 = 63,
|
||||
Z24S8 = 63,
|
||||
S8Z24 = 64,
|
||||
Z32FS8 = 65,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -193,6 +195,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
4, // ASTC_2D_5X5
|
||||
4, // ASTC_2D_5X5_SRGB
|
||||
4, // ASTC_2D_10X8
|
||||
4, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
@@ -208,70 +212,72 @@ static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
8, // ASTC_2D_8X5
|
||||
5, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
8, // ASTC_2D_8X5_SRGB
|
||||
5, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
8, // ASTC_2D_8X5
|
||||
5, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
8, // ASTC_2D_8X5_SRGB
|
||||
5, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
10, // ASTC_2D_10X8
|
||||
10, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
|
||||
return block_width_table[static_cast<std::size_t>(format)];
|
||||
@@ -341,6 +347,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
8, // ASTC_2D_10X8
|
||||
8, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
@@ -416,6 +424,8 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
|
||||
128, // ASTC_2D_5X4_SRGB
|
||||
128, // ASTC_2D_5X5
|
||||
128, // ASTC_2D_5X5_SRGB
|
||||
128, // ASTC_2D_10X8
|
||||
128, // ASTC_2D_10X8_SRGB
|
||||
32, // Z32F
|
||||
16, // Z16
|
||||
32, // Z24S8
|
||||
|
||||
@@ -202,6 +202,8 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
case TextureFormat::ASTC_2D_5X4:
|
||||
case TextureFormat::ASTC_2D_8X8:
|
||||
case TextureFormat::ASTC_2D_8X5:
|
||||
case TextureFormat::ASTC_2D_10X8:
|
||||
case TextureFormat::ASTC_2D_5X5:
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::A2B10G10R10:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
@@ -294,6 +296,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
case TextureFormat::BC6H_SF16:
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
case TextureFormat::ASTC_2D_8X8:
|
||||
case TextureFormat::ASTC_2D_5X5:
|
||||
case TextureFormat::ASTC_2D_10X8:
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::A2B10G10R10:
|
||||
case TextureFormat::A1B5G5R5:
|
||||
|
||||
@@ -168,20 +168,29 @@ struct TICEntry {
|
||||
|
||||
// High 16 bits of the pitch value
|
||||
BitField<0, 16, u32> pitch_high;
|
||||
|
||||
BitField<26, 1, u32> use_header_opt_control;
|
||||
BitField<27, 1, u32> depth_texture;
|
||||
BitField<28, 4, u32> max_mip_level;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> width_minus_1;
|
||||
BitField<22, 1, u32> srgb_conversion;
|
||||
BitField<23, 4, TextureType> texture_type;
|
||||
BitField<29, 3, u32> border_size;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> height_minus_1;
|
||||
BitField<16, 15, u32> depth_minus_1;
|
||||
};
|
||||
union {
|
||||
BitField<6, 13, u32> mip_lod_bias;
|
||||
BitField<27, 3, u32> max_anisotropy;
|
||||
};
|
||||
|
||||
INSERT_PADDING_BYTES(8);
|
||||
union {
|
||||
BitField<0, 4, u32> res_min_mip_level;
|
||||
BitField<4, 4, u32> res_max_mip_level;
|
||||
};
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
|
||||
|
||||
@@ -134,6 +134,14 @@ void Config::ReadValues() {
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
Settings::values.language_index = qt_config->value("language_index", 1).toInt();
|
||||
|
||||
const auto enabled = qt_config->value("rng_seed_enabled", false).toBool();
|
||||
if (enabled) {
|
||||
Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong();
|
||||
} else {
|
||||
Settings::values.rng_seed = std::nullopt;
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Miscellaneous");
|
||||
@@ -272,6 +280,10 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("current_user", Settings::values.current_user);
|
||||
|
||||
qt_config->setValue("language_index", Settings::values.language_index);
|
||||
|
||||
qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
|
||||
qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0));
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Miscellaneous");
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_general.h"
|
||||
#include "yuzu/configuration/configure_general.h"
|
||||
@@ -20,7 +24,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
this->setConfiguration();
|
||||
|
||||
ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
@@ -38,6 +41,30 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||
ui->widget->Populate(registry);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
|
||||
if (last_state == new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
// Message queue is shared between these services, we just need to signal an operation
|
||||
// change to one and it will handle both automatically
|
||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||
bool has_signalled = false;
|
||||
|
||||
if (applet_oe != nullptr) {
|
||||
applet_oe->GetMessageQueue()->OperationModeChanged();
|
||||
has_signalled = true;
|
||||
}
|
||||
|
||||
if (applet_ae != nullptr && !has_signalled) {
|
||||
applet_ae->GetMessageQueue()->OperationModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
@@ -45,6 +72,9 @@ void ConfigureGeneral::applyConfiguration() {
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode;
|
||||
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
|
||||
|
||||
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
||||
|
||||
@@ -137,6 +137,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
||||
connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
|
||||
connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
|
||||
|
||||
connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
|
||||
ui->rng_seed_edit->setEnabled(checked);
|
||||
if (!checked)
|
||||
ui->rng_seed_edit->setText(QStringLiteral("0000000000000000"));
|
||||
});
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
ui->current_user_icon->setScene(scene);
|
||||
|
||||
@@ -155,6 +161,14 @@ void ConfigureSystem::setConfiguration() {
|
||||
|
||||
PopulateUserList();
|
||||
UpdateCurrentUser();
|
||||
|
||||
ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
|
||||
ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
|
||||
|
||||
const auto rng_seed = QString("%1")
|
||||
.arg(Settings::values.rng_seed.value_or(0), 16, 16, QLatin1Char{'0'})
|
||||
.toUpper();
|
||||
ui->rng_seed_edit->setText(rng_seed);
|
||||
}
|
||||
|
||||
void ConfigureSystem::PopulateUserList() {
|
||||
@@ -195,6 +209,12 @@ void ConfigureSystem::applyConfiguration() {
|
||||
return;
|
||||
|
||||
Settings::values.language_index = ui->combo_language->currentIndex();
|
||||
|
||||
if (ui->rng_seed_checkbox->isChecked())
|
||||
Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
|
||||
else
|
||||
Settings::values.rng_seed = std::nullopt;
|
||||
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
@@ -240,7 +260,7 @@ void ConfigureSystem::RefreshConsoleID() {
|
||||
|
||||
void ConfigureSystem::SelectUser(const QModelIndex& index) {
|
||||
Settings::values.current_user =
|
||||
std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1);
|
||||
std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
|
||||
|
||||
UpdateCurrentUser();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<width>366</width>
|
||||
<height>483</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -22,98 +22,6 @@
|
||||
<string>System Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_language">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_birthday">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_console_id">
|
||||
<property name="text">
|
||||
<string>Console ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthmonth">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>January</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>February</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>March</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>April</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>May</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>June</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>July</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>August</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>September</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>October</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>November</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>December</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthday"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="combo_language">
|
||||
<property name="toolTip">
|
||||
@@ -206,6 +114,13 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_console_id">
|
||||
<property name="text">
|
||||
<string>Console ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_sound">
|
||||
<property name="text">
|
||||
@@ -213,6 +128,100 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_birthday">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthmonth">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>January</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>February</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>March</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>April</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>May</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>June</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>July</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>August</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>September</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>October</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>November</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>December</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthday"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="button_regenerate_console_id">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Regenerate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
@@ -232,19 +241,38 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="button_regenerate_console_id">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_language">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="rng_seed_checkbox">
|
||||
<property name="text">
|
||||
<string>RNG Seed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="rng_seed_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Lucida Console</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Regenerate</string>
|
||||
<property name="inputMask">
|
||||
<string>HHHHHHHHHHHHHHHH</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -933,7 +933,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
|
||||
const auto full = res == "Full";
|
||||
const auto entry_size = CalculateRomFSEntrySize(extracted, full);
|
||||
|
||||
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
|
||||
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
|
||||
static_cast<s32>(entry_size), this);
|
||||
progress.setWindowModality(Qt::WindowModal);
|
||||
progress.setMinimumDuration(100);
|
||||
|
||||
@@ -1621,7 +1622,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ui.action_Fullscreen->isChecked()) {
|
||||
if (!ui.action_Fullscreen->isChecked()) {
|
||||
UISettings::values.geometry = saveGeometry();
|
||||
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
|
||||
}
|
||||
|
||||
@@ -132,6 +132,13 @@ void Config::ReadValues() {
|
||||
Settings::values.current_user = std::clamp<int>(
|
||||
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
|
||||
|
||||
const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
|
||||
if (enabled) {
|
||||
Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0);
|
||||
} else {
|
||||
Settings::values.rng_seed = std::nullopt;
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
|
||||
|
||||
@@ -178,6 +178,11 @@ use_docked_mode =
|
||||
# 1 (default): Yes, 0 : No
|
||||
enable_nfc =
|
||||
|
||||
# Sets the seed for the RNG generator built into the switch
|
||||
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
|
||||
rng_seed_enabled =
|
||||
rng_seed =
|
||||
|
||||
# Sets the account username, max length is 32 characters
|
||||
# yuzu (default)
|
||||
username = yuzu
|
||||
|
||||
Reference in New Issue
Block a user