Compare commits
12 Commits
master
...
mainline-0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ae8fe96a | ||
|
|
5bb2494306 | ||
|
|
1527ea18f8 | ||
|
|
c71844d224 | ||
|
|
0ded864000 | ||
|
|
d3069570ad | ||
|
|
30d397f778 | ||
|
|
07fdad8d81 | ||
|
|
7162470cce | ||
|
|
b801ad78e7 | ||
|
|
decd0a012f | ||
|
|
2a8d14285f |
@@ -28,18 +28,14 @@ __declspec(noinline, noreturn)
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
|
||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
@@ -58,8 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
if (!writable_event) {
|
||||
// Create event if not provided
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
|
||||
"HLE Pause Event: " + reason);
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
|
||||
writable_event = pair.writable;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -78,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
// thread.
|
||||
ASSERT(requesting_thread == current_thread);
|
||||
|
||||
const u32 addr_value = Memory::Read32(address);
|
||||
u32 addr_value = Memory::Read32(address);
|
||||
|
||||
// If the mutex isn't being held, just return success.
|
||||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
||||
@@ -89,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// This a workaround where an unknown bug writes the mutex value to give ownership to a cond var
|
||||
// waiting thread.
|
||||
if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) {
|
||||
if (holding_thread->GetMutexWaitAddress() == address) {
|
||||
Release(address, holding_thread.get());
|
||||
addr_value = Memory::Read32(address);
|
||||
if (addr_value == 0)
|
||||
return RESULT_SUCCESS;
|
||||
else {
|
||||
holding_thread = handle_table.Get<Thread>(addr_value & Mutex::MutexOwnerMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the mutex is released
|
||||
current_thread->SetMutexWaitAddress(address);
|
||||
current_thread->SetWaitHandle(requesting_thread_handle);
|
||||
@@ -104,14 +119,13 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Mutex::Release(VAddr address) {
|
||||
ResultCode Mutex::Release(VAddr address, Thread* holding_thread) {
|
||||
// The mutex address must be 4-byte aligned
|
||||
if ((address % sizeof(u32)) != 0) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address);
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
@@ -120,7 +134,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
}
|
||||
|
||||
// Transfer the ownership of the mutex from the previous owner to the new one.
|
||||
TransferMutexOwnership(address, current_thread, thread);
|
||||
TransferMutexOwnership(address, holding_thread, thread);
|
||||
|
||||
u32 mutex_value = thread->GetWaitHandle();
|
||||
|
||||
@@ -141,7 +155,10 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
thread->SetWaitHandle(0);
|
||||
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
|
||||
system.PrepareReschedule();
|
||||
if (thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
if (holding_thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
Handle requesting_thread_handle);
|
||||
|
||||
/// Releases the mutex at the specified address.
|
||||
ResultCode Release(VAddr address);
|
||||
ResultCode Release(VAddr address, Thread* holding_thread);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
@@ -32,11 +32,6 @@ enum class HandleType : u32 {
|
||||
ServerSession,
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
Automatic, ///< Reset automatically on object acquisition
|
||||
Manual, ///< Never reset automatically
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
public:
|
||||
explicit Object(KernelCore& kernel);
|
||||
|
||||
@@ -20,15 +20,13 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||
|
||||
void ReadableEvent::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::Automatic) {
|
||||
signaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadableEvent::Signal() {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
if (!signaled) {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
};
|
||||
}
|
||||
|
||||
void ReadableEvent::Clear() {
|
||||
|
||||
@@ -27,10 +27,6 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
@@ -55,8 +51,7 @@ private:
|
||||
|
||||
void Signal();
|
||||
|
||||
ResetType reset_type;
|
||||
bool signaled;
|
||||
bool signaled{};
|
||||
|
||||
std::string name; ///< Name of event (optional)
|
||||
};
|
||||
|
||||
@@ -578,7 +578,8 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
|
||||
}
|
||||
|
||||
auto* const current_process = system.Kernel().CurrentProcess();
|
||||
return current_process->GetMutex().Release(mutex_addr);
|
||||
return current_process->GetMutex().Release(mutex_addr,
|
||||
system.CurrentScheduler().GetCurrentThread());
|
||||
}
|
||||
|
||||
enum class BreakType : u32 {
|
||||
@@ -1615,12 +1616,14 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
|
||||
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
ASSERT(thread);
|
||||
|
||||
const auto release_result = current_process->GetMutex().Release(mutex_addr);
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
|
||||
const auto release_result =
|
||||
current_process->GetMutex().Release(mutex_addr, current_thread.get());
|
||||
if (release_result.IsError()) {
|
||||
return release_result;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->SetCondVarWaitAddress(condition_variable_addr);
|
||||
current_thread->SetMutexWaitAddress(mutex_addr);
|
||||
current_thread->SetWaitHandle(thread_handle);
|
||||
@@ -2099,7 +2102,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
const auto [readable_event, writable_event] =
|
||||
WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
|
||||
WritableEvent::CreateEventPair(kernel, "CreateEvent");
|
||||
|
||||
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace Kernel {
|
||||
WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {}
|
||||
WritableEvent::~WritableEvent() = default;
|
||||
|
||||
EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name) {
|
||||
EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) {
|
||||
SharedPtr<WritableEvent> writable_event(new WritableEvent(kernel));
|
||||
SharedPtr<ReadableEvent> readable_event(new ReadableEvent(kernel));
|
||||
|
||||
@@ -24,7 +23,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_typ
|
||||
writable_event->readable = readable_event;
|
||||
readable_event->name = name + ":Readable";
|
||||
readable_event->signaled = false;
|
||||
readable_event->reset_type = reset_type;
|
||||
|
||||
return {std::move(readable_event), std::move(writable_event)};
|
||||
}
|
||||
@@ -33,10 +31,6 @@ SharedPtr<ReadableEvent> WritableEvent::GetReadableEvent() const {
|
||||
return readable;
|
||||
}
|
||||
|
||||
ResetType WritableEvent::GetResetType() const {
|
||||
return readable->reset_type;
|
||||
}
|
||||
|
||||
void WritableEvent::Signal() {
|
||||
readable->Signal();
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ public:
|
||||
/**
|
||||
* Creates an event
|
||||
* @param kernel The kernel instance to create this event under.
|
||||
* @param reset_type ResetType describing how to create event
|
||||
* @param name Optional name of event
|
||||
*/
|
||||
static EventPair CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name = "Unknown");
|
||||
static EventPair CreateEventPair(KernelCore& kernel, std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "WritableEvent";
|
||||
@@ -44,8 +42,6 @@ public:
|
||||
|
||||
SharedPtr<ReadableEvent> GetReadableEvent() const;
|
||||
|
||||
ResetType GetResetType() const;
|
||||
|
||||
void Signal();
|
||||
void Clear();
|
||||
bool IsSignaled() const;
|
||||
|
||||
@@ -289,8 +289,8 @@ ISelfController::ISelfController(Core::System& system,
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
launchable_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ISelfController:LaunchableEvent");
|
||||
|
||||
// This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
|
||||
// called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
|
||||
@@ -298,7 +298,7 @@ ISelfController::ISelfController(Core::System& system,
|
||||
// suspended if the event has previously been created by a call to
|
||||
// GetAccumulatedSuspendedTickChangedEvent.
|
||||
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
accumulated_suspended_tick_changed_event.writable->Signal();
|
||||
}
|
||||
|
||||
@@ -523,10 +523,10 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
|
||||
on_new_message =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
@@ -1091,7 +1091,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
}
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
state_changed_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
@@ -67,8 +67,8 @@ AOC_U::AOC_U(Core::System& system)
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
aoc_change_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() = default;
|
||||
|
||||
@@ -65,8 +65,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
|
||||
buffer_event =
|
||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||
audio_params.channel_count, std::move(unique_name),
|
||||
|
||||
@@ -47,8 +47,8 @@ public:
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
system_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
|
||||
system_event =
|
||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||
system.CoreTiming(), audren_params, system_event.writable, instance_number);
|
||||
}
|
||||
@@ -180,17 +180,17 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
buffer_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IAudioOutBufferReleasedEvent");
|
||||
|
||||
// Should be similar to audio_output_device_switch_event
|
||||
audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent");
|
||||
kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
|
||||
|
||||
// Should only be signalled when an audio output device has been changed, example: speaker
|
||||
// to headset
|
||||
audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
|
||||
kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace Service::BCAT {
|
||||
ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
|
||||
std::string_view event_name) {
|
||||
event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
|
||||
kernel, std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
|
||||
|
||||
@@ -34,8 +34,7 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(kernel, "BT:RegisterEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,14 +57,12 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ScanEvent");
|
||||
connection_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ConfigEvent");
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ScanEvent");
|
||||
connection_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConfigEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -40,7 +40,10 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
const auto res = base->GetDirectoryRelative(dir_name);
|
||||
if (res == nullptr)
|
||||
return base->CreateDirectoryRelative(dir_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
notification_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
|
||||
system.Kernel(), "INotificationService:NotifyEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -174,7 +174,7 @@ void Controller_NPad::OnInit() {
|
||||
auto& kernel = system.Kernel();
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
|
||||
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -203,13 +203,13 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
|
||||
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
|
||||
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
|
||||
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
|
||||
{123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
|
||||
{123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
|
||||
{124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
|
||||
{125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
|
||||
{126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
|
||||
{127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
|
||||
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
|
||||
{129, nullptr, "GetNpadHandheldActivationMode"},
|
||||
{129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
|
||||
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
|
||||
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
|
||||
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
|
||||
@@ -557,10 +557,126 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto npad_joy_device_type{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
npad_id, applet_resource_user_id, npad_joy_device_type);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto unknown_1{rp.Pop<u32>()};
|
||||
const auto unknown_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
|
||||
unknown_1, unknown_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StartLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StopLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto mode{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
|
||||
applet_resource_user_id, mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
||||
applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_1{rp.Pop<u32>()};
|
||||
const auto npad_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
|
||||
applet_resource_user_id, npad_1, npad_2);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (controller.SwapNpadAssignment(npad_1, npad_2)) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
LOG_ERROR(Service_HID, "Npads are not connected!");
|
||||
rb.Push(ERR_NPAD_NOT_CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
@@ -635,47 +751,6 @@ void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto unknown_1{rp.Pop<u32>()};
|
||||
const auto unknown_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
|
||||
unknown_1, unknown_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto mode{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
|
||||
applet_resource_user_id, mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
@@ -769,49 +844,6 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StartLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StopLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_1{rp.Pop<u32>()};
|
||||
const auto npad_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
|
||||
applet_resource_user_id, npad_1, npad_2);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (controller.SwapNpadAssignment(npad_1, npad_2)) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
LOG_ERROR(Service_HID, "Npads are not connected!");
|
||||
rb.Push(ERR_NPAD_NOT_CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
public:
|
||||
explicit HidDbg() : ServiceFramework{"hid:dbg"} {
|
||||
|
||||
@@ -106,14 +106,19 @@ private:
|
||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
|
||||
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
|
||||
void PermitVibration(Kernel::HLERequestContext& ctx);
|
||||
@@ -123,9 +128,6 @@ private:
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
Core::System& system;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::LBL {
|
||||
|
||||
@@ -18,21 +20,21 @@ public:
|
||||
explicit LBL() : ServiceFramework{"lbl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SaveCurrentSetting"},
|
||||
{1, nullptr, "LoadCurrentSetting"},
|
||||
{2, nullptr, "SetCurrentBrightnessSetting"},
|
||||
{3, nullptr, "GetCurrentBrightnessSetting"},
|
||||
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, nullptr, "SwitchBacklightOn"},
|
||||
{7, nullptr, "SwitchBacklightOff"},
|
||||
{8, nullptr, "GetBacklightSwitchStatus"},
|
||||
{9, nullptr, "EnableDimming"},
|
||||
{10, nullptr, "DisableDimming"},
|
||||
{11, nullptr, "IsDimmingEnabled"},
|
||||
{12, nullptr, "EnableAutoBrightnessControl"},
|
||||
{13, nullptr, "DisableAutoBrightnessControl"},
|
||||
{14, nullptr, "IsAutoBrightnessControlEnabled"},
|
||||
{0, &LBL::SaveCurrentSetting, "SaveCurrentSetting"},
|
||||
{1, &LBL::LoadCurrentSetting, "LoadCurrentSetting"},
|
||||
{2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
|
||||
{3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
|
||||
{4, &LBL::ApplyCurrentBrightnessSettingToBacklight, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, &LBL::GetBrightnessSettingAppliedToBacklight, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
|
||||
{7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
|
||||
{8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
|
||||
{9, &LBL::EnableDimming, "EnableDimming"},
|
||||
{10, &LBL::DisableDimming, "DisableDimming"},
|
||||
{11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
|
||||
{12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
|
||||
{13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
|
||||
{14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
|
||||
{15, nullptr, "SetAmbientLightSensorValue"},
|
||||
{16, nullptr, "GetAmbientLightSensorValue"},
|
||||
{17, nullptr, "SetBrightnessReflectionDelayLevel"},
|
||||
@@ -42,8 +44,8 @@ public:
|
||||
{21, nullptr, "SetCurrentAmbientLightSensorMapping"},
|
||||
{22, nullptr, "GetCurrentAmbientLightSensorMapping"},
|
||||
{23, nullptr, "IsAmbientLightSensorAvailable"},
|
||||
{24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{26, &LBL::EnableVrMode, "EnableVrMode"},
|
||||
{27, &LBL::DisableVrMode, "DisableVrMode"},
|
||||
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
@@ -53,13 +55,209 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void LoadFromSettings() {
|
||||
current_brightness = Settings::values.backlight_brightness;
|
||||
current_vr_mode_brightness = Settings::values.backlight_brightness;
|
||||
|
||||
if (auto_brightness_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vr_mode_enabled) {
|
||||
Renderer().SetCurrentBrightness(current_vr_mode_brightness);
|
||||
} else {
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
f32 GetAutoBrightnessValue() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& Renderer() {
|
||||
return Core::System::GetInstance().Renderer();
|
||||
}
|
||||
|
||||
void SaveCurrentSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
Settings::values.backlight_brightness = current_brightness;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void LoadCurrentSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
LoadFromSettings();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto value = rp.PopRaw<f32>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, value={:.3f}", value);
|
||||
|
||||
current_brightness = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current_brightness);
|
||||
}
|
||||
|
||||
void ApplyCurrentBrightnessSettingToBacklight(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
if (!auto_brightness_enabled) {
|
||||
Renderer().SetCurrentBrightness(vr_mode_enabled ? current_vr_mode_brightness
|
||||
: current_brightness);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBrightnessSettingAppliedToBacklight(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Renderer().GetCurrentResultantBrightness());
|
||||
}
|
||||
|
||||
void SwitchBacklightOn(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, fade_time={:016X}", fade_time);
|
||||
|
||||
Renderer().SetBacklightStatus(true, fade_time);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SwitchBacklightOff(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, fade_time={:016X}", fade_time);
|
||||
|
||||
Renderer().SetBacklightStatus(false, fade_time);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(Renderer().GetBacklightStatus());
|
||||
}
|
||||
|
||||
void EnableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
dimming_enabled = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "callled");
|
||||
|
||||
dimming_enabled = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsDimmingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(dimming_enabled);
|
||||
}
|
||||
|
||||
void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
auto_brightness_enabled = true;
|
||||
Renderer().SetCurrentBrightness(GetAutoBrightnessValue());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
auto_brightness_enabled = false;
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(auto_brightness_enabled);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto value = rp.PopRaw<f32>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, value={:.3f}", value);
|
||||
|
||||
current_vr_mode_brightness = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
if (vr_mode_enabled && !auto_brightness_enabled) {
|
||||
Renderer().SetCurrentBrightness(value);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current_vr_mode_brightness);
|
||||
}
|
||||
|
||||
void EnableVrMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (!vr_mode_enabled && !auto_brightness_enabled &&
|
||||
current_brightness != current_vr_mode_brightness) {
|
||||
Renderer().SetCurrentBrightness(current_vr_mode_brightness);
|
||||
}
|
||||
|
||||
vr_mode_enabled = true;
|
||||
}
|
||||
|
||||
@@ -69,6 +267,11 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (vr_mode_enabled && !auto_brightness_enabled &&
|
||||
current_brightness != current_vr_mode_brightness) {
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
}
|
||||
|
||||
vr_mode_enabled = false;
|
||||
}
|
||||
|
||||
@@ -80,9 +283,27 @@ private:
|
||||
rb.Push(vr_mode_enabled);
|
||||
}
|
||||
|
||||
bool auto_brightness_enabled = false;
|
||||
bool dimming_enabled = true;
|
||||
|
||||
f32 current_brightness = GetAutoBrightnessValue();
|
||||
f32 current_vr_mode_brightness = GetAutoBrightnessValue();
|
||||
|
||||
bool vr_mode_enabled = false;
|
||||
};
|
||||
|
||||
void RequestLoadCurrentSetting(SM::ServiceManager& sm) {
|
||||
if (&sm == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto lbl = sm.GetService<LBL>("lbl");
|
||||
|
||||
if (lbl) {
|
||||
lbl->LoadFromSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<LBL>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class ServiceManager;
|
||||
|
||||
namespace Service::LBL {
|
||||
|
||||
// Requests the LBL service passed to load brightness values from Settings
|
||||
void RequestLoadCurrentSetting(SM::ServiceManager& sm);
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::LBL
|
||||
|
||||
@@ -26,8 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)), system(system) {
|
||||
auto& kernel = system.Kernel();
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IUser:NFCTagDetected");
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:NFCTagDetected");
|
||||
}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
@@ -66,10 +65,9 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:DeactivateEvent");
|
||||
availability_change_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, "IUser:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::NIFM {
|
||||
|
||||
@@ -69,10 +70,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event2");
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event2");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -88,7 +87,12 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(RequestState::Connected);
|
||||
|
||||
if (Settings::values.bcat_backend == "none") {
|
||||
rb.PushEnum(RequestState::NotSubmitted);
|
||||
} else {
|
||||
rb.PushEnum(RequestState::Connected);
|
||||
}
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
@@ -196,14 +200,22 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(1);
|
||||
if (Settings::values.bcat_backend == "none") {
|
||||
rb.Push<u8>(0);
|
||||
} else {
|
||||
rb.Push<u8>(1);
|
||||
}
|
||||
}
|
||||
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(1);
|
||||
if (Settings::values.bcat_backend == "none") {
|
||||
rb.Push<u8>(0);
|
||||
} else {
|
||||
rb.Push<u8>(1);
|
||||
}
|
||||
}
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -141,8 +141,7 @@ public:
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
finished_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -22,6 +22,18 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
|
||||
switch (static_cast<IoctlCommand>(command.raw)) {
|
||||
case IoctlCommand::IocSetNVMAPfdCommand:
|
||||
return SetNVMAPfd(input, output);
|
||||
case IoctlCommand::IocSubmit:
|
||||
return Submit(input, output);
|
||||
case IoctlCommand::IocGetSyncpoint:
|
||||
return GetSyncpoint(input, output);
|
||||
case IoctlCommand::IocGetWaitbase:
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocMapBuffer:
|
||||
return MapBuffer(input, output);
|
||||
case IoctlCommand::IocMapBufferEx:
|
||||
return MapBufferEx(input, output);
|
||||
case IoctlCommand::IocUnmapBufferEx:
|
||||
return UnmapBufferEx(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
@@ -30,11 +42,67 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
|
||||
|
||||
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSetNvmapFD params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD));
|
||||
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSubmit params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetSyncpoint params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlUnmapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -23,16 +23,66 @@ public:
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
IocSetNVMAPfdCommand = 0x40044801,
|
||||
IocSubmit = 0xC0400001,
|
||||
IocGetSyncpoint = 0xC0080002,
|
||||
IocGetWaitbase = 0xC0080003,
|
||||
IocMapBuffer = 0xC01C0009,
|
||||
IocMapBufferEx = 0xC0A40009,
|
||||
IocUnmapBufferEx = 0xC0A4000A,
|
||||
};
|
||||
|
||||
struct IoctlSetNvmapFD {
|
||||
u32_le nvmap_fd;
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
struct IoctlSubmit {
|
||||
INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
|
||||
|
||||
struct IoctlGetSyncpoint {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
|
||||
|
||||
struct IoctlMapBuffer {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
|
||||
|
||||
struct IoctlMapBufferEx {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
|
||||
|
||||
struct IoctlUnmapBufferEx {
|
||||
INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
|
||||
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -22,6 +22,18 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
|
||||
switch (static_cast<IoctlCommand>(command.raw)) {
|
||||
case IoctlCommand::IocSetNVMAPfdCommand:
|
||||
return SetNVMAPfd(input, output);
|
||||
case IoctlCommand::IocSubmit:
|
||||
return Submit(input, output);
|
||||
case IoctlCommand::IocGetSyncpoint:
|
||||
return GetSyncpoint(input, output);
|
||||
case IoctlCommand::IocGetWaitbase:
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocMapBuffer:
|
||||
return MapBuffer(input, output);
|
||||
case IoctlCommand::IocMapBufferEx:
|
||||
return MapBuffer(input, output);
|
||||
case IoctlCommand::IocUnmapBufferEx:
|
||||
return UnmapBufferEx(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
@@ -30,11 +42,67 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
|
||||
|
||||
u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSetNvmapFD params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD));
|
||||
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSubmit params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetSyncpoint params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlUnmapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -23,6 +23,12 @@ public:
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
IocSetNVMAPfdCommand = 0x40044801,
|
||||
IocSubmit = 0xC0400001,
|
||||
IocGetSyncpoint = 0xC0080002,
|
||||
IocGetWaitbase = 0xC0080003,
|
||||
IocMapBuffer = 0xC01C0009,
|
||||
IocMapBufferEx = 0xC03C0009,
|
||||
IocUnmapBufferEx = 0xC03C000A,
|
||||
};
|
||||
|
||||
struct IoctlSetNvmapFD {
|
||||
@@ -30,9 +36,53 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
struct IoctlSubmit {
|
||||
INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size");
|
||||
|
||||
struct IoctlGetSyncpoint {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
|
||||
|
||||
struct IoctlMapBuffer {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
|
||||
|
||||
struct IoctlMapBufferEx {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size");
|
||||
|
||||
struct IoctlUnmapBufferEx {
|
||||
INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
|
||||
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -40,8 +40,7 @@ Module::Module(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
|
||||
events_interface.events[i] =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, event_label);
|
||||
events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
|
||||
events_interface.status[i] = EventState::Free;
|
||||
events_interface.registered[i] = false;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
|
||||
: id(id), layer_id(layer_id) {
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"BufferQueue NativeHandle");
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Service::VI {
|
||||
|
||||
Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
|
||||
auto& kernel = system.Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
vsync_event =
|
||||
Kernel::WritableEvent::CreateEventPair(kernel, fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
Display::~Display() = default;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
@@ -70,6 +72,7 @@ void Apply() {
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
if (system_instance.IsPoweredOn()) {
|
||||
system_instance.Renderer().RefreshBaseSettings();
|
||||
Service::LBL::RequestLoadCurrentSetting(system_instance.ServiceManager());
|
||||
}
|
||||
|
||||
Service::HID::ReloadInputDevices();
|
||||
|
||||
@@ -428,6 +428,8 @@ struct Values {
|
||||
float bg_green;
|
||||
float bg_blue;
|
||||
|
||||
float backlight_brightness = 0.5f;
|
||||
|
||||
std::string log_filter;
|
||||
|
||||
bool use_dev_keys;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
using BufferInfo = std::pair<const TBufferType*, u64>;
|
||||
|
||||
BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
|
||||
bool is_written = false) {
|
||||
bool is_written = false, bool use_fast_cbuf = false) {
|
||||
std::lock_guard lock{mutex};
|
||||
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
@@ -43,9 +43,13 @@ public:
|
||||
// Cache management is a big overhead, so only cache entries with a given size.
|
||||
// TODO: Figure out which size is the best for given games.
|
||||
constexpr std::size_t max_stream_size = 0x800;
|
||||
if (size < max_stream_size) {
|
||||
if (use_fast_cbuf || size < max_stream_size) {
|
||||
if (!is_written && !IsRegionWritten(cache_addr, cache_addr + size - 1)) {
|
||||
return StreamBufferUpload(host_ptr, size, alignment);
|
||||
if (use_fast_cbuf) {
|
||||
return ConstBufferUpload(host_ptr, size);
|
||||
} else {
|
||||
return StreamBufferUpload(host_ptr, size, alignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +156,10 @@ protected:
|
||||
virtual void CopyBlock(const TBuffer& src, const TBuffer& dst, std::size_t src_offset,
|
||||
std::size_t dst_offset, std::size_t size) = 0;
|
||||
|
||||
virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Register an object into the cache
|
||||
void Register(const MapInterval& new_map, bool inherit_written = false) {
|
||||
const CacheAddr cache_ptr = new_map->GetStart();
|
||||
|
||||
@@ -28,6 +28,13 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) {
|
||||
const u32 line_a = src_2 - src_1;
|
||||
const u32 line_b = dst_2 - dst_1;
|
||||
const u32 excess = std::max<s32>(0, line_a - src_line + src_1);
|
||||
return {line_b - (excess * line_b) / line_a, excess};
|
||||
}
|
||||
|
||||
void Fermi2D::HandleSurfaceCopy() {
|
||||
LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}",
|
||||
static_cast<u32>(regs.operation));
|
||||
@@ -47,10 +54,27 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
src_blit_x2 = static_cast<u32>((regs.blit_src_x >> 32) + regs.blit_dst_width);
|
||||
src_blit_y2 = static_cast<u32>((regs.blit_src_y >> 32) + regs.blit_dst_height);
|
||||
}
|
||||
u32 dst_blit_x2 = regs.blit_dst_x + regs.blit_dst_width;
|
||||
u32 dst_blit_y2 = regs.blit_dst_y + regs.blit_dst_height;
|
||||
const auto [new_dst_w, src_excess_x] =
|
||||
DelimitLine(src_blit_x1, src_blit_x2, regs.blit_dst_x, dst_blit_x2, regs.src.width);
|
||||
const auto [new_dst_h, src_excess_y] =
|
||||
DelimitLine(src_blit_y1, src_blit_y2, regs.blit_dst_y, dst_blit_y2, regs.src.height);
|
||||
dst_blit_x2 = new_dst_w + regs.blit_dst_x;
|
||||
src_blit_x2 = src_blit_x2 - src_excess_x;
|
||||
dst_blit_y2 = new_dst_h + regs.blit_dst_y;
|
||||
src_blit_y2 = src_blit_y2 - src_excess_y;
|
||||
const auto [new_src_w, dst_excess_x] =
|
||||
DelimitLine(regs.blit_dst_x, dst_blit_x2, src_blit_x1, src_blit_x2, regs.dst.width);
|
||||
const auto [new_src_h, dst_excess_y] =
|
||||
DelimitLine(regs.blit_dst_y, dst_blit_y2, src_blit_y1, src_blit_y2, regs.dst.height);
|
||||
src_blit_x2 = new_src_w + src_blit_x1;
|
||||
dst_blit_x2 = dst_blit_x2 - dst_excess_x;
|
||||
src_blit_y2 = new_src_h + src_blit_y1;
|
||||
dst_blit_y2 = dst_blit_y2 - dst_excess_y;
|
||||
const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
|
||||
const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y,
|
||||
regs.blit_dst_x + regs.blit_dst_width,
|
||||
regs.blit_dst_y + regs.blit_dst_height};
|
||||
const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2,
|
||||
dst_blit_y2};
|
||||
Config copy_config;
|
||||
copy_config.operation = regs.operation;
|
||||
copy_config.filter = regs.blit_control.filter;
|
||||
|
||||
@@ -40,4 +40,35 @@ void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
|
||||
renderer_settings.screenshot_requested = true;
|
||||
}
|
||||
|
||||
f32 RendererBase::GetCurrentResultantBrightness() const {
|
||||
return renderer_settings.current_brightness / 2.0f;
|
||||
}
|
||||
|
||||
void RendererBase::SetBacklightStatus(bool enabled, u64 fade_transition_time) {
|
||||
if (fade_transition_time == 0) {
|
||||
// Needed to ensure the renderer recognizes that a change must occur.
|
||||
fade_transition_time = 1;
|
||||
}
|
||||
|
||||
if (enabled && renderer_settings.current_brightness == 0) {
|
||||
renderer_settings.current_brightness = current_brightness_backup;
|
||||
renderer_settings.backlight_fade_time = fade_transition_time;
|
||||
} else if (!enabled && renderer_settings.current_brightness != 0) {
|
||||
current_brightness_backup = renderer_settings.current_brightness;
|
||||
renderer_settings.current_brightness = 0;
|
||||
renderer_settings.backlight_fade_time = fade_transition_time;
|
||||
}
|
||||
}
|
||||
|
||||
bool RendererBase::GetBacklightStatus() const {
|
||||
return renderer_settings.current_brightness != 0;
|
||||
}
|
||||
|
||||
void RendererBase::SetCurrentBrightness(f32 value) {
|
||||
if (value != renderer_settings.current_brightness) {
|
||||
renderer_settings.current_brightness = value * 2.0f;
|
||||
renderer_settings.backlight_fade_time = 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -28,6 +28,10 @@ struct RendererSettings {
|
||||
void* screenshot_bits;
|
||||
std::function<void()> screenshot_complete_callback;
|
||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||
|
||||
// Backlight & Brightness
|
||||
std::atomic<f32> current_brightness{1.f};
|
||||
std::atomic<u64> backlight_fade_time{0};
|
||||
};
|
||||
|
||||
class RendererBase : NonCopyable {
|
||||
@@ -86,6 +90,17 @@ public:
|
||||
void RequestScreenshot(void* data, std::function<void()> callback,
|
||||
const Layout::FramebufferLayout& layout);
|
||||
|
||||
// Gets the current brightness, even if it has been changed from the set value. Most of the time
|
||||
// for yuzu this will simply match what was returned, but implementations are free to change the
|
||||
// value in settings.
|
||||
f32 GetCurrentResultantBrightness() const;
|
||||
|
||||
void SetBacklightStatus(bool enabled, u64 fade_transition_time);
|
||||
|
||||
bool GetBacklightStatus() const;
|
||||
|
||||
void SetCurrentBrightness(f32 value);
|
||||
|
||||
protected:
|
||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||
std::unique_ptr<RasterizerInterface> rasterizer;
|
||||
@@ -97,6 +112,9 @@ protected:
|
||||
private:
|
||||
/// Updates the framebuffer layout of the contained render window handle.
|
||||
void UpdateCurrentFramebufferLayout();
|
||||
|
||||
// Value of brightness before backlight switch used to preserve value.
|
||||
f32 current_brightness_backup;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128));
|
||||
|
||||
CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t size)
|
||||
@@ -26,11 +30,22 @@ CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t siz
|
||||
CachedBufferBlock::~CachedBufferBlock() = default;
|
||||
|
||||
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
std::size_t stream_size)
|
||||
: VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>{
|
||||
rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {}
|
||||
const Device& device, std::size_t stream_size)
|
||||
: GenericBufferCache{rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {
|
||||
if (!device.HasFastBufferSubData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OGLBufferCache::~OGLBufferCache() = default;
|
||||
static constexpr auto size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize);
|
||||
glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
|
||||
for (const GLuint cbuf : cbufs) {
|
||||
glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
OGLBufferCache::~OGLBufferCache() {
|
||||
glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
|
||||
}
|
||||
|
||||
Buffer OGLBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) {
|
||||
return std::make_shared<CachedBufferBlock>(cache_addr, size);
|
||||
@@ -69,4 +84,12 @@ void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t
|
||||
static_cast<GLsizeiptr>(size));
|
||||
}
|
||||
|
||||
OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer,
|
||||
std::size_t size) {
|
||||
DEBUG_ASSERT(cbuf_cursor < std::size(cbufs));
|
||||
const GLuint& cbuf = cbufs[cbuf_cursor++];
|
||||
glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer);
|
||||
return {&cbuf, 0};
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
@@ -18,12 +20,14 @@ class System;
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device;
|
||||
class OGLStreamBuffer;
|
||||
class RasterizerOpenGL;
|
||||
|
||||
class CachedBufferBlock;
|
||||
|
||||
using Buffer = std::shared_ptr<CachedBufferBlock>;
|
||||
using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
|
||||
|
||||
class CachedBufferBlock : public VideoCommon::BufferBlock {
|
||||
public:
|
||||
@@ -38,14 +42,18 @@ private:
|
||||
OGLBuffer gl_buffer{};
|
||||
};
|
||||
|
||||
class OGLBufferCache final : public VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer> {
|
||||
class OGLBufferCache final : public GenericBufferCache {
|
||||
public:
|
||||
explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
std::size_t stream_size);
|
||||
const Device& device, std::size_t stream_size);
|
||||
~OGLBufferCache();
|
||||
|
||||
const GLuint* GetEmptyBuffer(std::size_t) override;
|
||||
|
||||
void Acquire() noexcept {
|
||||
cbuf_cursor = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override;
|
||||
|
||||
@@ -61,6 +69,14 @@ protected:
|
||||
|
||||
void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
|
||||
std::size_t dst_offset, std::size_t size) override;
|
||||
|
||||
BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override;
|
||||
|
||||
private:
|
||||
std::size_t cbuf_cursor = 0;
|
||||
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers *
|
||||
Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram>
|
||||
cbufs;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -51,8 +51,11 @@ bool HasExtension(const std::vector<std::string_view>& images, std::string_view
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device() {
|
||||
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const std::vector extensions = GetExtensions();
|
||||
|
||||
const bool is_nvidia = vendor == "NVIDIA Corporation";
|
||||
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
@@ -64,6 +67,7 @@ Device::Device() {
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = TestComponentIndexingBug();
|
||||
has_precise_bug = TestPreciseBug();
|
||||
has_fast_buffer_sub_data = is_nvidia;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
return has_precise_bug;
|
||||
}
|
||||
|
||||
bool HasFastBufferSubData() const {
|
||||
return has_fast_buffer_sub_data;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestComponentIndexingBug();
|
||||
@@ -69,6 +73,7 @@ private:
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
bool has_precise_bug{};
|
||||
bool has_fast_buffer_sub_data{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -67,7 +67,7 @@ static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buf
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device},
|
||||
system{system}, screen_info{info}, buffer_cache{*this, system, STREAM_BUFFER_SIZE} {
|
||||
system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
state.draw.shader_program = 0;
|
||||
state.Apply();
|
||||
@@ -257,10 +257,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
|
||||
|
||||
GLShader::MaxwellUniformData ubo{};
|
||||
ubo.SetFromRegs(gpu, stage);
|
||||
ubo.SetFromRegs(gpu);
|
||||
const auto [buffer, offset] =
|
||||
buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
|
||||
|
||||
@@ -269,10 +267,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
|
||||
Shader shader{shader_cache.GetStageProgram(program)};
|
||||
|
||||
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
|
||||
SetupDrawConstBuffers(stage_enum, shader);
|
||||
SetupDrawGlobalMemory(stage_enum, shader);
|
||||
const auto texture_buffer_usage{SetupDrawTextures(stage_enum, shader, base_bindings)};
|
||||
// Stage indices are 0 - 5
|
||||
const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
|
||||
SetupDrawConstBuffers(stage, shader);
|
||||
SetupDrawGlobalMemory(stage, shader);
|
||||
const auto texture_buffer_usage{SetupDrawTextures(stage, shader, base_bindings)};
|
||||
|
||||
const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
|
||||
const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
|
||||
@@ -558,6 +557,8 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
SyncPolygonOffset();
|
||||
SyncAlphaTest();
|
||||
|
||||
buffer_cache.Acquire();
|
||||
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
@@ -879,7 +880,8 @@ void RasterizerOpenGL::SetupConstBuffer(const Tegra::Engines::ConstBufferInfo& b
|
||||
const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4));
|
||||
|
||||
const auto alignment = device.GetUniformBufferAlignment();
|
||||
const auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment);
|
||||
const auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment, false,
|
||||
device.HasFastBufferSubData());
|
||||
bind_ubo_pushbuffer.Push(cbuf, offset, size);
|
||||
}
|
||||
|
||||
@@ -1057,6 +1059,15 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
}
|
||||
state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0;
|
||||
state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
|
||||
|
||||
bool flip_y = false;
|
||||
if (regs.viewport_transform[0].scale_y < 0.0) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
if (regs.screen_y_control.y_negate != 0) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled(
|
||||
@@ -1079,26 +1090,24 @@ void RasterizerOpenGL::SyncClipCoef() {
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
|
||||
const auto& regs = maxwell3d.regs;
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
|
||||
state.cull.enabled = regs.cull.enabled != 0;
|
||||
if (state.cull.enabled) {
|
||||
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
|
||||
state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
|
||||
}
|
||||
|
||||
const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 ||
|
||||
regs.viewport_transform[0].scale_y < 0.0f};
|
||||
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
|
||||
|
||||
// If the GPU is configured to flip the rasterized triangles, then we need to flip the
|
||||
// notion of front and back. Note: We flip the triangles when the value of the register is 0
|
||||
// because OpenGL already does it for us.
|
||||
if (flip_triangles) {
|
||||
if (state.cull.front_face == GL_CCW)
|
||||
state.cull.front_face = GL_CW;
|
||||
else if (state.cull.front_face == GL_CW)
|
||||
state.cull.front_face = GL_CCW;
|
||||
// If the GPU is configured to flip the rasterized triangles, then we need to flip the
|
||||
// notion of front and back.
|
||||
const bool flip_triangles{regs.screen_y_control.triangle_rast_flip != 0 &&
|
||||
regs.viewport_transform[0].scale_y > 0.0f};
|
||||
if (flip_triangles) {
|
||||
if (state.cull.front_face == GL_CCW) {
|
||||
state.cull.front_face = GL_CW;
|
||||
} else if (state.cull.front_face == GL_CW) {
|
||||
state.cull.front_face = GL_CCW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1872,10 +1872,6 @@ private:
|
||||
Expression EmitVertex(Operation operation) {
|
||||
ASSERT_MSG(stage == ProgramType::Geometry,
|
||||
"EmitVertex is expected to be used in a geometry shader.");
|
||||
|
||||
// If a geometry shader is attached, it will always flip (it's the last stage before
|
||||
// fragment). For more info about flipping, refer to gl_shader_gen.cpp.
|
||||
code.AddLine("gl_Position.xy *= viewport_flip.xy;");
|
||||
code.AddLine("EmitVertex();");
|
||||
return {};
|
||||
}
|
||||
@@ -1883,14 +1879,12 @@ private:
|
||||
Expression EndPrimitive(Operation operation) {
|
||||
ASSERT_MSG(stage == ProgramType::Geometry,
|
||||
"EndPrimitive is expected to be used in a geometry shader.");
|
||||
|
||||
code.AddLine("EndPrimitive();");
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression YNegate(Operation operation) {
|
||||
// Config pack's third value is Y_NEGATE's state.
|
||||
return {"config_pack[2]", Type::Uint};
|
||||
return {"y_negate", Type::Float};
|
||||
}
|
||||
|
||||
template <u32 element>
|
||||
|
||||
@@ -20,8 +20,7 @@ std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const
|
||||
std::string out = GetCommonDeclarations();
|
||||
out += R"(
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
float y_direction;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -35,23 +34,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
void main() {
|
||||
execute_vertex();
|
||||
)";
|
||||
|
||||
if (ir_b) {
|
||||
out += " execute_vertex_b();";
|
||||
}
|
||||
|
||||
out += R"(
|
||||
|
||||
// Set Position Y direction
|
||||
gl_Position.y *= utof(config_pack[2]);
|
||||
// Check if the flip stage is VertexB
|
||||
// Config pack's second value is flip_stage
|
||||
if (config_pack[1] == 1) {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
gl_Position.xy *= viewport_flip.xy;
|
||||
}
|
||||
}
|
||||
)";
|
||||
out += "}\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -59,8 +45,7 @@ std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
|
||||
std::string out = GetCommonDeclarations();
|
||||
out += R"(
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
float y_direction;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -87,8 +72,7 @@ layout (location = 6) out vec4 FragColor6;
|
||||
layout (location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
float y_direction;
|
||||
};
|
||||
|
||||
)";
|
||||
|
||||
@@ -40,27 +40,11 @@ void ProgramManager::UpdatePipeline() {
|
||||
old_state = current_state;
|
||||
}
|
||||
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell) {
|
||||
const auto& regs = maxwell.regs;
|
||||
const auto& state = maxwell.state;
|
||||
|
||||
// TODO(bunnei): Support more than one viewport
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
|
||||
|
||||
instance_id = state.current_instance;
|
||||
|
||||
// Assign in which stage the position has to be flipped
|
||||
// (the last stage before the fragment shader).
|
||||
constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
|
||||
if (maxwell.regs.shader_config[geometry_index].enable) {
|
||||
flip_stage = geometry_index;
|
||||
} else {
|
||||
flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
|
||||
}
|
||||
|
||||
// Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
|
||||
y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f;
|
||||
y_direction = regs.screen_y_control.y_negate == 0 ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader
|
||||
|
||||
@@ -18,17 +18,12 @@ namespace OpenGL::GLShader {
|
||||
/// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
|
||||
/// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
/// Not following that rule will cause problems on some AMD drivers.
|
||||
struct MaxwellUniformData {
|
||||
void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
|
||||
struct alignas(16) MaxwellUniformData {
|
||||
void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell);
|
||||
|
||||
alignas(16) GLvec4 viewport_flip;
|
||||
struct alignas(16) {
|
||||
GLuint instance_id;
|
||||
GLuint flip_stage;
|
||||
GLfloat y_direction;
|
||||
};
|
||||
GLfloat y_direction;
|
||||
};
|
||||
static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) == 16, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
||||
@@ -410,6 +410,12 @@ void OpenGLState::ApplyAlphaTest() {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyClipControl() {
|
||||
if (UpdateValue(cur_state.clip_control.origin, clip_control.origin)) {
|
||||
glClipControl(clip_control.origin, GL_NEGATIVE_ONE_TO_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTextures() {
|
||||
if (const auto update = UpdateArray(cur_state.textures, textures)) {
|
||||
glBindTextures(update->first, update->second, textures.data() + update->first);
|
||||
@@ -453,6 +459,7 @@ void OpenGLState::Apply() {
|
||||
ApplyImages();
|
||||
ApplyPolygonOffset();
|
||||
ApplyAlphaTest();
|
||||
ApplyClipControl();
|
||||
}
|
||||
|
||||
void OpenGLState::EmulateViewportWithScissor() {
|
||||
|
||||
@@ -146,6 +146,10 @@ public:
|
||||
|
||||
std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
|
||||
|
||||
struct {
|
||||
GLenum origin = GL_LOWER_LEFT;
|
||||
} clip_control;
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
@@ -182,6 +186,7 @@ public:
|
||||
void ApplyDepthClamp();
|
||||
void ApplyPolygonOffset();
|
||||
void ApplyAlphaTest();
|
||||
void ApplyClipControl();
|
||||
|
||||
/// Resets any references to the given resource
|
||||
OpenGLState& UnbindTexture(GLuint handle);
|
||||
|
||||
@@ -54,11 +54,13 @@ in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
uniform vec4 backlight;
|
||||
|
||||
void main() {
|
||||
// Swap RGBA -> ABGR so we don't have to do this on the CPU. This needs to change if we have to
|
||||
// support more framebuffer pixel formats.
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
// Also multiply the color by the backlight multiplier supplied.
|
||||
color = texture(color_texture, frag_tex_coord) * backlight;
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -121,8 +123,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
||||
LoadFBToScreenInfo(*framebuffer);
|
||||
|
||||
if (renderer_settings.screenshot_requested)
|
||||
if (renderer_settings.screenshot_requested) {
|
||||
CaptureScreenshot();
|
||||
}
|
||||
|
||||
if (renderer_settings.backlight_fade_time > 0) {
|
||||
UpdateBacklight();
|
||||
}
|
||||
|
||||
DrawScreen(render_window.GetFramebufferLayout());
|
||||
|
||||
@@ -205,9 +212,13 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
state.Apply();
|
||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||
uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture");
|
||||
uniform_backlight = glGetUniformLocation(shader.handle, "backlight");
|
||||
attrib_position = glGetAttribLocation(shader.handle, "vert_position");
|
||||
attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
|
||||
|
||||
// Initialize backlight
|
||||
glUniform4f(uniform_backlight, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Generate VBO handle for drawing
|
||||
vertex_buffer.Create();
|
||||
|
||||
@@ -416,6 +427,29 @@ void RendererOpenGL::CaptureScreenshot() {
|
||||
renderer_settings.screenshot_requested = false;
|
||||
}
|
||||
|
||||
void RendererOpenGL::UpdateBacklight() {
|
||||
constexpr u64 PER_FRAME_FADE_TIME = 1000000000.0f / 60;
|
||||
|
||||
const auto fade_time = renderer_settings.backlight_fade_time.load(std::memory_order_relaxed);
|
||||
auto value = renderer_settings.current_brightness.load(std::memory_order_relaxed);
|
||||
if (fade_time <= PER_FRAME_FADE_TIME) {
|
||||
glUniform4f(uniform_backlight, value, value, value, value);
|
||||
renderer_settings.backlight_fade_time = 0;
|
||||
fade_time_max = 0;
|
||||
} else {
|
||||
if (fade_time_max == 0) {
|
||||
fade_time_max = fade_time;
|
||||
value_max = value;
|
||||
}
|
||||
|
||||
value += (value_max - value) * PER_FRAME_FADE_TIME / fade_time_max;
|
||||
|
||||
glUniform4f(uniform_backlight, value, value, value, value);
|
||||
renderer_settings.backlight_fade_time -= PER_FRAME_FADE_TIME;
|
||||
renderer_settings.current_brightness = value;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* GetSource(GLenum source) {
|
||||
#define RET(s) \
|
||||
case GL_DEBUG_SOURCE_##s: \
|
||||
|
||||
@@ -70,6 +70,7 @@ private:
|
||||
void UpdateFramerate();
|
||||
|
||||
void CaptureScreenshot();
|
||||
void UpdateBacklight();
|
||||
|
||||
// Loads framebuffer from emulated memory into the display information structure
|
||||
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
||||
@@ -97,6 +98,7 @@ private:
|
||||
// Shader uniform location indices
|
||||
GLuint uniform_modelview_matrix;
|
||||
GLuint uniform_color_texture;
|
||||
GLuint uniform_backlight;
|
||||
|
||||
// Shader attribute input indices
|
||||
GLuint attrib_position;
|
||||
@@ -105,6 +107,10 @@ private:
|
||||
/// Used for transforming the framebuffer orientation
|
||||
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
|
||||
Common::Rectangle<int> framebuffer_crop_rect;
|
||||
|
||||
// Used for backlight transitions
|
||||
u64 fade_time_max = 0;
|
||||
f32 value_max = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -250,6 +250,16 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
|
||||
return params;
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry) {
|
||||
return TextureTypeToSurfaceTarget(entry.GetType(), entry.IsArray());
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry) {
|
||||
return ImageTypeToSurfaceTarget(entry.GetType());
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsLayered() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
|
||||
@@ -41,6 +41,14 @@ public:
|
||||
static SurfaceParams CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry);
|
||||
|
||||
std::size_t Hash() const {
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
|
||||
@@ -94,10 +94,16 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
const auto gpu_addr{tic.Address()};
|
||||
if (!gpu_addr) {
|
||||
return {};
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto params{SurfaceParams::CreateForTexture(tic, entry)};
|
||||
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
|
||||
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
|
||||
if (guard_samplers) {
|
||||
sampled_textures.push_back(surface);
|
||||
}
|
||||
@@ -109,10 +115,15 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
const auto gpu_addr{tic.Address()};
|
||||
if (!gpu_addr) {
|
||||
return {};
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto params{SurfaceParams::CreateForImage(tic, entry)};
|
||||
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
|
||||
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
|
||||
if (guard_samplers) {
|
||||
sampled_textures.push_back(surface);
|
||||
}
|
||||
@@ -142,11 +153,17 @@ public:
|
||||
SetEmptyDepthBuffer();
|
||||
return {};
|
||||
}
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
SetEmptyDepthBuffer();
|
||||
return {};
|
||||
}
|
||||
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
||||
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
||||
auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents, true);
|
||||
auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
|
||||
if (depth_buffer.target)
|
||||
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
||||
depth_buffer.target = surface_view.first;
|
||||
@@ -179,8 +196,16 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents, true);
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
SetEmptyColorBuffer(index);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto surface_view =
|
||||
GetSurface(gpu_addr, cache_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents, true);
|
||||
if (render_targets[index].target)
|
||||
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
|
||||
render_targets[index].target = surface_view.first;
|
||||
@@ -229,8 +254,14 @@ public:
|
||||
const GPUVAddr src_gpu_addr = src_config.Address();
|
||||
const GPUVAddr dst_gpu_addr = dst_config.Address();
|
||||
DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
|
||||
std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false);
|
||||
std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false);
|
||||
const auto dst_host_ptr{system.GPU().MemoryManager().GetPointer(dst_gpu_addr)};
|
||||
const auto dst_cache_addr{ToCacheAddr(dst_host_ptr)};
|
||||
const auto src_host_ptr{system.GPU().MemoryManager().GetPointer(src_gpu_addr)};
|
||||
const auto src_cache_addr{ToCacheAddr(src_host_ptr)};
|
||||
std::pair<TSurface, TView> dst_surface =
|
||||
GetSurface(dst_gpu_addr, dst_cache_addr, dst_params, true, false);
|
||||
std::pair<TSurface, TView> src_surface =
|
||||
GetSurface(src_gpu_addr, src_cache_addr, src_params, true, false);
|
||||
ImageBlit(src_surface.second, dst_surface.second, copy_config);
|
||||
dst_surface.first->MarkAsModified(true, Tick());
|
||||
}
|
||||
@@ -346,13 +377,6 @@ protected:
|
||||
return new_surface;
|
||||
}
|
||||
|
||||
std::pair<TSurface, TView> GetFermiSurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||
SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config);
|
||||
const GPUVAddr gpu_addr = config.Address();
|
||||
return GetSurface(gpu_addr, params, true, false);
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
|
||||
private:
|
||||
@@ -615,22 +639,9 @@ private:
|
||||
* left blank.
|
||||
* @param is_render Whether or not the surface is a render target.
|
||||
**/
|
||||
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool preserve_contents, bool is_render) {
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
|
||||
// Step 0: guarantee a valid surface
|
||||
if (!cache_addr) {
|
||||
// Return a null surface if it's invalid
|
||||
SurfaceParams new_params = params;
|
||||
new_params.width = 1;
|
||||
new_params.height = 1;
|
||||
new_params.depth = 1;
|
||||
new_params.block_height = 0;
|
||||
new_params.block_depth = 0;
|
||||
return InitializeSurface(gpu_addr, new_params, false);
|
||||
}
|
||||
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr,
|
||||
const SurfaceParams& params, bool preserve_contents,
|
||||
bool is_render) {
|
||||
|
||||
// Step 1
|
||||
// Check Level 1 Cache for a fast structural match. If candidate surface
|
||||
@@ -794,6 +805,42 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a null surface based on a target texture.
|
||||
* @param target The target of the null surface.
|
||||
**/
|
||||
TView GetNullSurface(SurfaceTarget target) {
|
||||
const u32 i_target = static_cast<u32>(target);
|
||||
if (const auto it = invalid_cache.find(i_target); it != invalid_cache.end()) {
|
||||
return it->second->GetMainView();
|
||||
}
|
||||
SurfaceParams params{};
|
||||
params.target = target;
|
||||
params.is_tiled = false;
|
||||
params.srgb_conversion = false;
|
||||
params.is_layered = false;
|
||||
params.block_width = 0;
|
||||
params.block_height = 0;
|
||||
params.block_depth = 0;
|
||||
params.tile_width_spacing = 1;
|
||||
params.width = 1;
|
||||
params.height = 1;
|
||||
params.depth = 1;
|
||||
params.pitch = 4;
|
||||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
params.pixel_format = VideoCore::Surface::PixelFormat::RGBA16F;
|
||||
params.component_type = VideoCore::Surface::ComponentType::Float;
|
||||
params.type = VideoCore::Surface::SurfaceType::ColorTexture;
|
||||
auto surface = CreateSurface(0ULL, params);
|
||||
invalid_memory.clear();
|
||||
invalid_memory.resize(surface->GetHostSizeInBytes(), 0U);
|
||||
surface->UploadTexture(invalid_memory);
|
||||
surface->MarkAsModified(false, Tick());
|
||||
invalid_cache.emplace(i_target, surface);
|
||||
return surface->GetMainView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the a source and destination starting address and parameters,
|
||||
* and tries to deduce if they are supposed to be depth textures. If so, their
|
||||
@@ -991,6 +1038,11 @@ private:
|
||||
|
||||
std::vector<TSurface> sampled_textures;
|
||||
|
||||
/// This cache stores null surfaces in order to be used as a placeholder
|
||||
/// for invalid texture calls.
|
||||
std::unordered_map<u32, TSurface> invalid_cache;
|
||||
std::vector<u8> invalid_memory{};
|
||||
|
||||
StagingCache staging_cache;
|
||||
std::recursive_mutex mutex;
|
||||
};
|
||||
|
||||
@@ -62,6 +62,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
}
|
||||
UpdateBackgroundColorButton(new_bg_color);
|
||||
});
|
||||
connect(ui->brightness_reset, &QPushButton::pressed, this,
|
||||
[this] { ui->brightness_slider->setValue(100); });
|
||||
}
|
||||
|
||||
ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
@@ -80,6 +82,7 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue));
|
||||
ui->brightness_slider->setValue(Settings::values.backlight_brightness * 100 + 50);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
@@ -93,6 +96,7 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.bg_red = static_cast<float>(bg_color.redF());
|
||||
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
|
||||
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
|
||||
Settings::values.backlight_brightness = (ui->brightness_slider->value() - 50.0f) / 100.0f;
|
||||
}
|
||||
|
||||
void ConfigureGraphics::changeEvent(QEvent* event) {
|
||||
|
||||
@@ -111,6 +111,68 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Brightness</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="brightness_slider">
|
||||
<property name="minimum">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>150</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::NoTicks</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="brightness_reset">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -172,17 +172,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() con
|
||||
return list;
|
||||
}
|
||||
|
||||
QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) {
|
||||
switch (reset_type) {
|
||||
case Kernel::ResetType::Automatic:
|
||||
return tr("automatic reset");
|
||||
case Kernel::ResetType::Manual:
|
||||
return tr("manual reset");
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
WaitTreeObjectList::WaitTreeObjectList(
|
||||
const std::vector<Kernel::SharedPtr<Kernel::WaitObject>>& list, bool w_all)
|
||||
: object_list(list), wait_all(w_all) {}
|
||||
@@ -336,16 +325,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeWaitObject(object) {}
|
||||
WaitTreeEvent::~WaitTreeEvent() = default;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(
|
||||
tr("reset type = %1")
|
||||
.arg(GetResetTypeQString(
|
||||
static_cast<const Kernel::ReadableEvent&>(object).GetResetType()))));
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
|
||||
: thread_list(list) {}
|
||||
WaitTreeThreadList::~WaitTreeThreadList() = default;
|
||||
|
||||
@@ -111,8 +111,6 @@ public:
|
||||
|
||||
protected:
|
||||
const Kernel::WaitObject& object;
|
||||
|
||||
static QString GetResetTypeQString(Kernel::ResetType reset_type);
|
||||
};
|
||||
|
||||
class WaitTreeObjectList : public WaitTreeExpandableItem {
|
||||
@@ -146,8 +144,6 @@ class WaitTreeEvent : public WaitTreeWaitObject {
|
||||
public:
|
||||
explicit WaitTreeEvent(const Kernel::ReadableEvent& object);
|
||||
~WaitTreeEvent() override;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
};
|
||||
|
||||
class WaitTreeThreadList : public WaitTreeExpandableItem {
|
||||
|
||||
@@ -817,6 +817,9 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() {
|
||||
if (!GLAD_GL_ARB_multi_bind) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_clip_control) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_clip_control"));
|
||||
}
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc) {
|
||||
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
};
|
||||
|
||||
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
std::vector<std::string> unsupported_ext;
|
||||
std::vector<std::string_view> unsupported_ext;
|
||||
|
||||
if (!GLAD_GL_ARB_buffer_storage)
|
||||
unsupported_ext.push_back("ARB_buffer_storage");
|
||||
@@ -62,6 +62,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
|
||||
if (!GLAD_GL_ARB_multi_bind)
|
||||
unsupported_ext.push_back("ARB_multi_bind");
|
||||
if (!GLAD_GL_ARB_clip_control)
|
||||
unsupported_ext.push_back("ARB_clip_control");
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc)
|
||||
@@ -71,8 +73,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
if (!GLAD_GL_ARB_depth_buffer_float)
|
||||
unsupported_ext.push_back("ARB_depth_buffer_float");
|
||||
|
||||
for (const std::string& ext : unsupported_ext)
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
|
||||
for (const auto& extension : unsupported_ext)
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
|
||||
|
||||
return unsupported_ext.empty();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user