Compare commits
54 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26f8a700a7 | ||
|
|
13222f94c0 | ||
|
|
b0b027d2d0 | ||
|
|
db21ac2627 | ||
|
|
d6f5f5cafa | ||
|
|
e597665569 | ||
|
|
a3cdd773c3 | ||
|
|
4596ef5274 | ||
|
|
1a302d4d47 | ||
|
|
35480167b1 | ||
|
|
0b594f3344 | ||
|
|
92b18345a8 | ||
|
|
ba14fb42e4 | ||
|
|
1d11def9c4 | ||
|
|
5078106068 | ||
|
|
1710847d08 | ||
|
|
0db8918947 | ||
|
|
c3affdd162 | ||
|
|
09727a6a97 | ||
|
|
c6c1c1b45f | ||
|
|
0c59e6265f | ||
|
|
3c3d9afd61 | ||
|
|
69884d8a8f | ||
|
|
4c818b60e3 | ||
|
|
52c326c301 | ||
|
|
52bb524526 | ||
|
|
9f803299de | ||
|
|
3b84e04af1 | ||
|
|
ba38d91fe2 | ||
|
|
f58a6152fc | ||
|
|
49a3571e76 | ||
|
|
7e92497402 | ||
|
|
679e63550a | ||
|
|
3d81a8efd5 | ||
|
|
d9f9bb7552 | ||
|
|
f45c25aaba | ||
|
|
2561a79c39 | ||
|
|
ac603cf130 | ||
|
|
8dbb8edcd3 | ||
|
|
c608d3a979 | ||
|
|
932922f67f | ||
|
|
cb2ce9924a | ||
|
|
72c48e01c9 | ||
|
|
5e636d1f6e | ||
|
|
b2b98b2f44 | ||
|
|
62e08c30b7 | ||
|
|
3e1a9a45a6 | ||
|
|
a9268286a4 | ||
|
|
65f3908064 | ||
|
|
6957bac03c | ||
|
|
7185d90a53 | ||
|
|
fc6d46c374 | ||
|
|
7b9c982d29 | ||
|
|
e0b0947437 |
@@ -6,6 +6,8 @@ TRAVIS_BRANCH
|
||||
TRAVIS_BUILD_ID
|
||||
TRAVIS_BUILD_NUMBER
|
||||
TRAVIS_COMMIT
|
||||
TRAVIS_COMMIT_RANGE
|
||||
TRAVIS_EVENT_TYPE
|
||||
TRAVIS_JOB_ID
|
||||
TRAVIS_JOB_NUMBER
|
||||
TRAVIS_REPO_SLUG
|
||||
|
||||
@@ -68,7 +68,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f};
|
||||
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,11 @@ private:
|
||||
|
||||
/// Push ///
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s32 value) {
|
||||
cmdbuf[index++] = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(u32 value) {
|
||||
cmdbuf[index++] = value;
|
||||
@@ -234,6 +239,22 @@ inline void ResponseBuilder::Push(ResultCode value) {
|
||||
Push<u32>(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s8 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s16 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s64 value) {
|
||||
Push(static_cast<u32>(value));
|
||||
Push(static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(u8 value) {
|
||||
PushRaw(value);
|
||||
|
||||
@@ -44,8 +44,4 @@ ResultCode ReadableEvent::Reset() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ReadableEvent::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -39,8 +39,6 @@ public:
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
/// Unconditionally clears the readable event's state.
|
||||
void Clear();
|
||||
|
||||
|
||||
@@ -66,10 +66,6 @@ void Timer::Clear() {
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void Timer::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
void Timer::Signal(int cycles_late) {
|
||||
LOG_TRACE(Kernel, "Timer {} fired", GetObjectId());
|
||||
|
||||
|
||||
@@ -51,8 +51,6 @@ public:
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
/**
|
||||
* Starts the timer, with the specified initial delay and interval.
|
||||
* @param initial Delay until the timer is first fired
|
||||
|
||||
@@ -33,19 +33,19 @@ public:
|
||||
* Add a thread to wait on this object
|
||||
* @param thread Pointer to thread to add
|
||||
*/
|
||||
virtual void AddWaitingThread(SharedPtr<Thread> thread);
|
||||
void AddWaitingThread(SharedPtr<Thread> thread);
|
||||
|
||||
/**
|
||||
* Removes a thread from waiting on this object (e.g. if it was resumed already)
|
||||
* @param thread Pointer to thread to remove
|
||||
*/
|
||||
virtual void RemoveWaitingThread(Thread* thread);
|
||||
void RemoveWaitingThread(Thread* thread);
|
||||
|
||||
/**
|
||||
* Wake up all threads waiting on this object that can be awoken, in priority order,
|
||||
* and set the synchronization result and output of the thread.
|
||||
*/
|
||||
virtual void WakeupAllWaitingThreads();
|
||||
void WakeupAllWaitingThreads();
|
||||
|
||||
/**
|
||||
* Wakes up a single thread waiting on this object.
|
||||
|
||||
@@ -249,7 +249,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{350, nullptr, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{401, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
{410, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
{1000, nullptr, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Service::Audio {
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
IAudioIn() : ServiceFramework("IAudioIn") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetAudioInState"},
|
||||
{1, nullptr, "StartAudioIn"},
|
||||
@@ -28,16 +29,24 @@ public:
|
||||
{12, nullptr, "SetAudioInDeviceGain"},
|
||||
{13, nullptr, "GetAudioInDeviceGain"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IAudioIn() = default;
|
||||
};
|
||||
|
||||
AudInU::AudInU() : ServiceFramework("audin:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListAudioIns"}, {1, nullptr, "OpenAudioIn"}, {2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"}, {4, nullptr, "ListAudioInsAuto"},
|
||||
{0, nullptr, "ListAudioIns"},
|
||||
{1, nullptr, "OpenAudioIn"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"},
|
||||
{4, nullptr, "ListAudioInsAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Service::Audio {
|
||||
class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> {
|
||||
public:
|
||||
IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetFinalOutputRecorderState"},
|
||||
{1, nullptr, "StartFinalOutputRecorder"},
|
||||
@@ -20,10 +21,13 @@ public:
|
||||
{4, nullptr, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
|
||||
{6, nullptr, "ContainsFinalOutputRecorderBuffer"},
|
||||
{7, nullptr, "Unknown"},
|
||||
{7, nullptr, "GetFinalOutputRecorderBufferEndTime"},
|
||||
{8, nullptr, "AppendFinalOutputRecorderBufferAuto"},
|
||||
{9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"},
|
||||
{10, nullptr, "FlushFinalOutputRecorderBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IFinalOutputRecorder() = default;
|
||||
|
||||
@@ -229,14 +229,16 @@ private:
|
||||
}; // namespace Audio
|
||||
|
||||
AudRenU::AudRenU() : ServiceFramework("audren:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
|
||||
{2, &AudRenU::GetAudioDevice, "GetAudioDevice"},
|
||||
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
|
||||
{3, nullptr, "OpenAudioRendererAuto"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo,
|
||||
"GetAudioDeviceServiceWithRevisionInfo"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -313,7 +315,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
|
||||
@@ -19,16 +19,16 @@ public:
|
||||
explicit Bt() : ServiceFramework{"bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, &Bt::RegisterEvent, "RegisterEvent"},
|
||||
{0, nullptr, "LeClientReadCharacteristic"},
|
||||
{1, nullptr, "LeClientReadDescriptor"},
|
||||
{2, nullptr, "LeClientWriteCharacteristic"},
|
||||
{3, nullptr, "LeClientWriteDescriptor"},
|
||||
{4, nullptr, "LeClientRegisterNotification"},
|
||||
{5, nullptr, "LeClientDeregisterNotification"},
|
||||
{6, nullptr, "SetLeResponse"},
|
||||
{7, nullptr, "LeSendIndication"},
|
||||
{8, nullptr, "GetLeEventInfo"},
|
||||
{9, &Bt::RegisterBleEvent, "RegisterBleEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterEvent(Kernel::HLERequestContext& ctx) {
|
||||
void RegisterBleEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -55,11 +55,11 @@ public:
|
||||
explicit BtDrv() : ServiceFramework{"btdrv"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown"},
|
||||
{1, nullptr, "Init"},
|
||||
{2, nullptr, "Enable"},
|
||||
{3, nullptr, "Disable"},
|
||||
{4, nullptr, "CleanupAndShutdown"},
|
||||
{0, nullptr, "InitializeBluetoothDriver"},
|
||||
{1, nullptr, "InitializeBluetooth"},
|
||||
{2, nullptr, "EnableBluetooth"},
|
||||
{3, nullptr, "DisableBluetooth"},
|
||||
{4, nullptr, "CleanupBluetooth"},
|
||||
{5, nullptr, "GetAdapterProperties"},
|
||||
{6, nullptr, "GetAdapterProperty"},
|
||||
{7, nullptr, "SetAdapterProperty"},
|
||||
@@ -70,36 +70,91 @@ public:
|
||||
{12, nullptr, "CancelBond"},
|
||||
{13, nullptr, "PinReply"},
|
||||
{14, nullptr, "SspReply"},
|
||||
{15, nullptr, "Unknown2"},
|
||||
{16, nullptr, "InitInterfaces"},
|
||||
{17, nullptr, "HidHostInterface_Connect"},
|
||||
{18, nullptr, "HidHostInterface_Disconnect"},
|
||||
{19, nullptr, "HidHostInterface_SendData"},
|
||||
{20, nullptr, "HidHostInterface_SendData2"},
|
||||
{21, nullptr, "HidHostInterface_SetReport"},
|
||||
{22, nullptr, "HidHostInterface_GetReport"},
|
||||
{23, nullptr, "HidHostInterface_WakeController"},
|
||||
{24, nullptr, "HidHostInterface_AddPairedDevice"},
|
||||
{25, nullptr, "HidHostInterface_GetPairedDevice"},
|
||||
{26, nullptr, "HidHostInterface_CleanupAndShutdown"},
|
||||
{27, nullptr, "Unknown3"},
|
||||
{28, nullptr, "ExtInterface_SetTSI"},
|
||||
{29, nullptr, "ExtInterface_SetBurstMode"},
|
||||
{30, nullptr, "ExtInterface_SetZeroRetran"},
|
||||
{31, nullptr, "ExtInterface_SetMcMode"},
|
||||
{32, nullptr, "ExtInterface_StartLlrMode"},
|
||||
{33, nullptr, "ExtInterface_ExitLlrMode"},
|
||||
{34, nullptr, "ExtInterface_SetRadio"},
|
||||
{35, nullptr, "ExtInterface_SetVisibility"},
|
||||
{36, nullptr, "Unknown4"},
|
||||
{37, nullptr, "Unknown5"},
|
||||
{38, nullptr, "HidHostInterface_GetLatestPlr"},
|
||||
{39, nullptr, "ExtInterface_GetPendingConnections"},
|
||||
{40, nullptr, "HidHostInterface_GetChannelMap"},
|
||||
{41, nullptr, "SetIsBluetoothBoostEnabled"},
|
||||
{42, nullptr, "GetIsBluetoothBoostEnabled"},
|
||||
{43, nullptr, "SetIsBluetoothAfhEnabled"},
|
||||
{44, nullptr, "GetIsBluetoothAfhEnabled"},
|
||||
{15, nullptr, "GetEventInfo"},
|
||||
{16, nullptr, "InitializeHid"},
|
||||
{17, nullptr, "HidConnect"},
|
||||
{18, nullptr, "HidDisconnect"},
|
||||
{19, nullptr, "HidSendData"},
|
||||
{20, nullptr, "HidSendData2"},
|
||||
{21, nullptr, "HidSetReport"},
|
||||
{22, nullptr, "HidGetReport"},
|
||||
{23, nullptr, "HidWakeController"},
|
||||
{24, nullptr, "HidAddPairedDevice"},
|
||||
{25, nullptr, "HidGetPairedDevice"},
|
||||
{26, nullptr, "CleanupHid"},
|
||||
{27, nullptr, "HidGetEventInfo"},
|
||||
{28, nullptr, "ExtSetTsi"},
|
||||
{29, nullptr, "ExtSetBurstMode"},
|
||||
{30, nullptr, "ExtSetZeroRetran"},
|
||||
{31, nullptr, "ExtSetMcMode"},
|
||||
{32, nullptr, "ExtStartLlrMode"},
|
||||
{33, nullptr, "ExtExitLlrMode"},
|
||||
{34, nullptr, "ExtSetRadio"},
|
||||
{35, nullptr, "ExtSetVisibility"},
|
||||
{36, nullptr, "ExtSetTbfcScan"},
|
||||
{37, nullptr, "RegisterHidReportEvent"},
|
||||
{38, nullptr, "HidGetReportEventInfo"},
|
||||
{39, nullptr, "GetLatestPlr"},
|
||||
{40, nullptr, "ExtGetPendingConnections"},
|
||||
{41, nullptr, "GetChannelMap"},
|
||||
{42, nullptr, "EnableBluetoothBoostSetting"},
|
||||
{43, nullptr, "IsBluetoothBoostSettingEnabled"},
|
||||
{44, nullptr, "EnableBluetoothAfhSetting"},
|
||||
{45, nullptr, "IsBluetoothAfhSettingEnabled"},
|
||||
{46, nullptr, "InitializeBluetoothLe"},
|
||||
{47, nullptr, "EnableBluetoothLe"},
|
||||
{48, nullptr, "DisableBluetoothLe"},
|
||||
{49, nullptr, "CleanupBluetoothLe"},
|
||||
{50, nullptr, "SetLeVisibility"},
|
||||
{51, nullptr, "SetLeConnectionParameter"},
|
||||
{52, nullptr, "SetLeDefaultConnectionParameter"},
|
||||
{53, nullptr, "SetLeAdvertiseData"},
|
||||
{54, nullptr, "SetLeAdvertiseParameter"},
|
||||
{55, nullptr, "StartLeScan"},
|
||||
{56, nullptr, "StopLeScan"},
|
||||
{57, nullptr, "AddLeScanFilterCondition"},
|
||||
{58, nullptr, "DeleteLeScanFilterCondition"},
|
||||
{59, nullptr, "DeleteLeScanFilter"},
|
||||
{60, nullptr, "ClearLeScanFilters"},
|
||||
{61, nullptr, "EnableLeScanFilter"},
|
||||
{62, nullptr, "RegisterLeClient"},
|
||||
{63, nullptr, "UnregisterLeClient"},
|
||||
{64, nullptr, "UnregisterLeClientAll"},
|
||||
{65, nullptr, "LeClientConnect"},
|
||||
{66, nullptr, "LeClientCancelConnection"},
|
||||
{67, nullptr, "LeClientDisconnect"},
|
||||
{68, nullptr, "LeClientGetAttributes"},
|
||||
{69, nullptr, "LeClientDiscoverService"},
|
||||
{70, nullptr, "LeClientConfigureMtu"},
|
||||
{71, nullptr, "RegisterLeServer"},
|
||||
{72, nullptr, "UnregisterLeServer"},
|
||||
{73, nullptr, "LeServerConnect"},
|
||||
{74, nullptr, "LeServerDisconnect"},
|
||||
{75, nullptr, "CreateLeService"},
|
||||
{76, nullptr, "StartLeService"},
|
||||
{77, nullptr, "AddLeCharacteristic"},
|
||||
{78, nullptr, "AddLeDescriptor"},
|
||||
{79, nullptr, "GetLeCoreEventInfo"},
|
||||
{80, nullptr, "LeGetFirstCharacteristic"},
|
||||
{81, nullptr, "LeGetNextCharacteristic"},
|
||||
{82, nullptr, "LeGetFirstDescriptor"},
|
||||
{83, nullptr, "LeGetNextDescriptor"},
|
||||
{84, nullptr, "RegisterLeCoreDataPath"},
|
||||
{85, nullptr, "UnregisterLeCoreDataPath"},
|
||||
{86, nullptr, "RegisterLeHidDataPath"},
|
||||
{87, nullptr, "UnregisterLeHidDataPath"},
|
||||
{88, nullptr, "RegisterLeDataPath"},
|
||||
{89, nullptr, "UnregisterLeDataPath"},
|
||||
{90, nullptr, "LeClientReadCharacteristic"},
|
||||
{91, nullptr, "LeClientReadDescriptor"},
|
||||
{92, nullptr, "LeClientWriteCharacteristic"},
|
||||
{93, nullptr, "LeClientWriteDescriptor"},
|
||||
{94, nullptr, "LeClientRegisterNotification"},
|
||||
{95, nullptr, "LeClientDeregisterNotification"},
|
||||
{96, nullptr, "GetLeHidEventInfo"},
|
||||
{97, nullptr, "RegisterBleHidEvent"},
|
||||
{98, nullptr, "SetLeScanParameter"},
|
||||
{256, nullptr, "GetIsManufacturingMode"}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -20,38 +20,38 @@ public:
|
||||
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "Unknown32"},
|
||||
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
|
||||
{34, nullptr, "Unknown34"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "Unknown36"},
|
||||
{37, nullptr, "Unknown37"},
|
||||
{0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
|
||||
{1, nullptr, "GetBleScanFilterParameter"},
|
||||
{2, nullptr, "GetBleScanFilterParameter2"},
|
||||
{3, nullptr, "StartBleScanForGeneral"},
|
||||
{4, nullptr, "StopBleScanForGeneral"},
|
||||
{5, nullptr, "GetBleScanResultsForGeneral"},
|
||||
{6, nullptr, "StartBleScanForPaired"},
|
||||
{7, nullptr, "StopBleScanForPaired"},
|
||||
{8, nullptr, "StartBleScanForSmartDevice"},
|
||||
{9, nullptr, "StopBleScanForSmartDevice"},
|
||||
{10, nullptr, "GetBleScanResultsForSmartDevice"},
|
||||
{17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"},
|
||||
{18, nullptr, "BleConnect"},
|
||||
{19, nullptr, "BleDisconnect"},
|
||||
{20, nullptr, "BleGetConnectionState"},
|
||||
{21, nullptr, "AcquireBlePairingEvent"},
|
||||
{22, nullptr, "BlePairDevice"},
|
||||
{23, nullptr, "BleUnPairDevice"},
|
||||
{24, nullptr, "BleUnPairDevice2"},
|
||||
{25, nullptr, "BleGetPairedDevices"},
|
||||
{26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"},
|
||||
{27, nullptr, "GetGattServices"},
|
||||
{28, nullptr, "GetGattService"},
|
||||
{29, nullptr, "GetGattIncludedServices"},
|
||||
{30, nullptr, "GetBelongingGattService"},
|
||||
{31, nullptr, "GetGattCharacteristics"},
|
||||
{32, nullptr, "GetGattDescriptors"},
|
||||
{33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"},
|
||||
{34, nullptr, "ConfigureBleMtu"},
|
||||
{35, nullptr, "GetBleMtu"},
|
||||
{36, nullptr, "RegisterBleGattDataPath"},
|
||||
{37, nullptr, "UnregisterBleGattDataPath"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
void AcquireBleScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
rb.PushCopyObjects(scan_event.readable);
|
||||
}
|
||||
|
||||
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
rb.PushCopyObjects(connection_event.readable);
|
||||
}
|
||||
|
||||
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
rb.PushCopyObjects(service_discovery.readable);
|
||||
}
|
||||
|
||||
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -111,14 +111,14 @@ public:
|
||||
explicit BTM_USR() : ServiceFramework{"btm:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
|
||||
{0, &BTM_USR::GetCore, "GetCore"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
void GetCore(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -134,26 +134,64 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"},
|
||||
{2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"},
|
||||
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"},
|
||||
{9, nullptr, "Unknown8"},
|
||||
{10, nullptr, "Unknown9"},
|
||||
{11, nullptr, "Unknown10"},
|
||||
{12, nullptr, "Unknown11"},
|
||||
{13, nullptr, "Unknown12"},
|
||||
{14, nullptr, "EnableRadioImpl"},
|
||||
{15, nullptr, "DisableRadioImpl"},
|
||||
{14, nullptr, "EnableRadio"},
|
||||
{15, nullptr, "DisableRadio"},
|
||||
{16, nullptr, "Unknown13"},
|
||||
{17, nullptr, "Unknown14"},
|
||||
{18, nullptr, "Unknown15"},
|
||||
{19, nullptr, "Unknown16"},
|
||||
{20, nullptr, "Unknown17"},
|
||||
{21, nullptr, "Unknown18"},
|
||||
{22, nullptr, "Unknown19"},
|
||||
{23, nullptr, "Unknown20"},
|
||||
{24, nullptr, "Unknown21"},
|
||||
{25, nullptr, "Unknown22"},
|
||||
{26, nullptr, "Unknown23"},
|
||||
{27, nullptr, "Unknown24"},
|
||||
{28, nullptr, "Unknown25"},
|
||||
{29, nullptr, "Unknown26"},
|
||||
{30, nullptr, "Unknown27"},
|
||||
{31, nullptr, "Unknown28"},
|
||||
{32, nullptr, "Unknown29"},
|
||||
{33, nullptr, "Unknown30"},
|
||||
{34, nullptr, "Unknown31"},
|
||||
{35, nullptr, "Unknown32"},
|
||||
{36, nullptr, "Unknown33"},
|
||||
{37, nullptr, "Unknown34"},
|
||||
{38, nullptr, "Unknown35"},
|
||||
{39, nullptr, "Unknown36"},
|
||||
{40, nullptr, "Unknown37"},
|
||||
{41, nullptr, "Unknown38"},
|
||||
{42, nullptr, "Unknown39"},
|
||||
{43, nullptr, "Unknown40"},
|
||||
{44, nullptr, "Unknown41"},
|
||||
{45, nullptr, "Unknown42"},
|
||||
{46, nullptr, "Unknown43"},
|
||||
{47, nullptr, "Unknown44"},
|
||||
{48, nullptr, "Unknown45"},
|
||||
{49, nullptr, "Unknown46"},
|
||||
{50, nullptr, "Unknown47"},
|
||||
{51, nullptr, "Unknown48"},
|
||||
{52, nullptr, "Unknown49"},
|
||||
{53, nullptr, "Unknown50"},
|
||||
{54, nullptr, "Unknown51"},
|
||||
{55, nullptr, "Unknown52"},
|
||||
{56, nullptr, "Unknown53"},
|
||||
{57, nullptr, "Unknown54"},
|
||||
{58, nullptr, "Unknown55"},
|
||||
{59, nullptr, "Unknown56"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -166,7 +204,7 @@ public:
|
||||
explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RegisterSystemEventForDiscoveryImpl"},
|
||||
{0, nullptr, "RegisterSystemEventForDiscovery"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
@@ -175,6 +213,10 @@ public:
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -187,16 +229,16 @@ public:
|
||||
explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "StartGamepadPairingImpl"},
|
||||
{1, nullptr, "CancelGamepadPairingImpl"},
|
||||
{2, nullptr, "ClearGamepadPairingDatabaseImpl"},
|
||||
{3, nullptr, "GetPairedGamepadCountImpl"},
|
||||
{4, nullptr, "EnableRadioImpl"},
|
||||
{5, nullptr, "DisableRadioImpl"},
|
||||
{6, nullptr, "GetRadioOnOffImpl"},
|
||||
{7, nullptr, "AcquireRadioEventImpl"},
|
||||
{8, nullptr, "AcquireGamepadPairingEventImpl"},
|
||||
{9, nullptr, "IsGamepadPairingStartedImpl"},
|
||||
{0, nullptr, "StartGamepadPairing"},
|
||||
{1, nullptr, "CancelGamepadPairing"},
|
||||
{2, nullptr, "ClearGamepadPairingDatabase"},
|
||||
{3, nullptr, "GetPairedGamepadCount"},
|
||||
{4, nullptr, "EnableRadio"},
|
||||
{5, nullptr, "DisableRadio"},
|
||||
{6, nullptr, "GetRadioOnOff"},
|
||||
{7, nullptr, "AcquireRadioEvent"},
|
||||
{8, nullptr, "AcquireGamepadPairingEvent"},
|
||||
{9, nullptr, "IsGamepadPairingStarted"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -209,7 +251,7 @@ public:
|
||||
explicit BTM_SYS() : ServiceFramework{"btm:sys"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"},
|
||||
{0, &BTM_SYS::GetCore, "GetCore"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -217,7 +259,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
void GetCore(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -627,8 +627,8 @@ private:
|
||||
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "MountContent"},
|
||||
{1, &FSP_SRV::Initialize, "Initialize"},
|
||||
{0, nullptr, "OpenFileSystem"},
|
||||
{1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"},
|
||||
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
|
||||
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
|
||||
{8, nullptr, "OpenFileSystemWithId"},
|
||||
@@ -637,10 +637,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{12, nullptr, "OpenBisStorage"},
|
||||
{13, nullptr, "InvalidateBisCache"},
|
||||
{17, nullptr, "OpenHostFileSystem"},
|
||||
{18, &FSP_SRV::MountSdCard, "MountSdCard"},
|
||||
{18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"},
|
||||
{19, nullptr, "FormatSdCardFileSystem"},
|
||||
{21, nullptr, "DeleteSaveDataFileSystem"},
|
||||
{22, &FSP_SRV::CreateSaveData, "CreateSaveData"},
|
||||
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
|
||||
{23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
|
||||
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
|
||||
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
|
||||
@@ -652,7 +652,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{32, nullptr, "ExtendSaveDataFileSystem"},
|
||||
{33, nullptr, "DeleteCacheStorage"},
|
||||
{34, nullptr, "GetCacheStorageSize"},
|
||||
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
|
||||
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
|
||||
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
|
||||
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
|
||||
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
|
||||
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
|
||||
@@ -664,21 +665,26 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
|
||||
{65, nullptr, "UpdateSaveDataMacForDebug"},
|
||||
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
|
||||
{67, nullptr, "FindSaveDataWithFilter"},
|
||||
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
|
||||
{80, nullptr, "OpenSaveDataMetaFile"},
|
||||
{81, nullptr, "OpenSaveDataTransferManager"},
|
||||
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
|
||||
{83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
|
||||
{84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
|
||||
{100, nullptr, "OpenImageDirectoryFileSystem"},
|
||||
{110, nullptr, "OpenContentStorageFileSystem"},
|
||||
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
|
||||
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
|
||||
{201, nullptr, "OpenDataStorageByProgramId"},
|
||||
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
|
||||
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
|
||||
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
|
||||
{400, nullptr, "OpenDeviceOperator"},
|
||||
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
|
||||
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
|
||||
{510, nullptr, "OpenSystemDataUpdateEventNotifier"},
|
||||
{511, nullptr, "NotifySystemDataUpdateEvent"},
|
||||
{520, nullptr, "SimulateGameCardDetectionEvent"},
|
||||
{600, nullptr, "SetCurrentPosixTime"},
|
||||
{601, nullptr, "QuerySaveDataTotalSize"},
|
||||
{602, nullptr, "VerifySaveDataFileSystem"},
|
||||
@@ -717,6 +723,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{1008, nullptr, "OpenRegisteredUpdatePartition"},
|
||||
{1009, nullptr, "GetAndClearMemoryReportInfo"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
|
||||
{1200, nullptr, "OpenMultiCommitManager"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -724,7 +732,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
|
||||
FSP_SRV::~FSP_SRV() = default;
|
||||
|
||||
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -743,7 +751,7 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultCode(-1));
|
||||
}
|
||||
|
||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IFileSystem filesystem(OpenSDMC().Unwrap());
|
||||
@@ -753,7 +761,7 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
|
||||
}
|
||||
|
||||
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
|
||||
@@ -767,7 +775,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
|
||||
@@ -793,7 +801,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
|
||||
MountSaveData(ctx);
|
||||
OpenSaveDataFileSystem(ctx);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
|
||||
@@ -881,7 +889,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto storage_id = rp.PopRaw<FileSys::StorageId>();
|
||||
|
||||
@@ -19,17 +19,17 @@ public:
|
||||
~FSP_SRV() override;
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void SetCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
|
||||
void MountSdCard(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void OpenSdCardFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
void OpenRomStorage(Kernel::HLERequestContext& ctx);
|
||||
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
{6, nullptr, "CloseContentStorageForcibly"},
|
||||
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
|
||||
{8, nullptr, "CleanupContentMetaDatabase"},
|
||||
{9, nullptr, "OpenContentStorage2"},
|
||||
{10, nullptr, "CloseContentStorage"},
|
||||
{11, nullptr, "OpenContentMetaDatabase2"},
|
||||
{12, nullptr, "CloseContentMetaDatabase"},
|
||||
{9, nullptr, "ActivateContentStorage"},
|
||||
{10, nullptr, "InactivateContentStorage"},
|
||||
{11, nullptr, "ActivateContentMetaDatabase"},
|
||||
{12, nullptr, "InactivateContentMetaDatabase"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplication"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
@@ -96,10 +96,10 @@ public:
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "Unknown1"},
|
||||
{92, nullptr, "Unknown2"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication2"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "Unknown3"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
@@ -219,10 +219,10 @@ public:
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "Unknown4"},
|
||||
{2050, nullptr, "Unknown5"},
|
||||
{2100, nullptr, "Unknown6"},
|
||||
{2101, nullptr, "Unknown7"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{2150, nullptr, "CreateRightsEnvironment"},
|
||||
{2151, nullptr, "DestroyRightsEnvironment"},
|
||||
{2152, nullptr, "ActivateRightsEnvironment"},
|
||||
@@ -237,10 +237,10 @@ public:
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
{2200, nullptr, "Unknown8"},
|
||||
{2201, nullptr, "Unknown9"},
|
||||
{2250, nullptr, "Unknown10"},
|
||||
{2300, nullptr, "Unknown11"},
|
||||
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -355,6 +355,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -389,6 +390,11 @@ public:
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -403,7 +409,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment "},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -22,7 +21,6 @@
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
@@ -30,12 +28,6 @@ constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Add the different displays to the list of displays.
|
||||
displays.emplace_back(0, "Default");
|
||||
displays.emplace_back(1, "External");
|
||||
displays.emplace_back(2, "Edid");
|
||||
displays.emplace_back(3, "Internal");
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
@@ -55,13 +47,13 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(std::string_view name) {
|
||||
LOG_WARNING(Service, "Opening display {}", name);
|
||||
LOG_DEBUG(Service, "Opening \"{}\" display", name);
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
ASSERT(name == "Default");
|
||||
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.name == name; });
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.name == name; });
|
||||
|
||||
ASSERT(itr != displays.end());
|
||||
|
||||
@@ -69,48 +61,66 @@ u64 NVFlinger::OpenDisplay(std::string_view name) {
|
||||
}
|
||||
|
||||
u64 NVFlinger::CreateLayer(u64 display_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
auto& display = FindDisplay(display_id);
|
||||
|
||||
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
|
||||
|
||||
u64 layer_id = next_layer_id++;
|
||||
u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
const u64 layer_id = next_layer_id++;
|
||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
|
||||
display.layers.emplace_back(layer_id, buffer_queue);
|
||||
buffer_queues.emplace_back(std::move(buffer_queue));
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
const auto& layer = GetLayer(display_id, layer_id);
|
||||
u32 NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
|
||||
const auto& layer = FindLayer(display_id, layer_id);
|
||||
return layer.buffer_queue->GetId();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) {
|
||||
return GetDisplay(display_id).vsync_event.readable;
|
||||
return FindDisplay(display_id).vsync_event.readable;
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
|
||||
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||
[&](const auto& queue) { return queue->GetId() == id; });
|
||||
std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const {
|
||||
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||
[&](const auto& queue) { return queue->GetId() == id; });
|
||||
|
||||
ASSERT(itr != buffer_queues.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Display& NVFlinger::GetDisplay(u64 display_id) {
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
Display& NVFlinger::FindDisplay(u64 display_id) {
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
|
||||
ASSERT(itr != displays.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
const Display& NVFlinger::FindDisplay(u64 display_id) const {
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
|
||||
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
ASSERT(itr != displays.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Layer& NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
|
||||
auto& display = FindDisplay(display_id);
|
||||
|
||||
const auto itr = std::find_if(display.layers.begin(), display.layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
ASSERT(itr != display.layers.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
const Layer& NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
|
||||
const auto& display = FindDisplay(display_id);
|
||||
|
||||
const auto itr = std::find_if(display.layers.begin(), display.layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
ASSERT(itr != display.layers.end());
|
||||
return *itr;
|
||||
@@ -145,7 +155,7 @@ void NVFlinger::Compose() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& igbp_buffer = buffer->get().igbp_buffer;
|
||||
const auto& igbp_buffer = buffer->get().igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -56,35 +57,47 @@ public:
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// Opens the specified display and returns the id.
|
||||
/// Opens the specified display and returns the ID.
|
||||
u64 OpenDisplay(std::string_view name);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer id.
|
||||
/// Creates a layer on the specified display and returns the layer ID.
|
||||
u64 CreateLayer(u64 display_id);
|
||||
|
||||
/// Gets the buffer queue id of the specified layer in the specified display.
|
||||
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
|
||||
/// Finds the buffer queue ID of the specified layer in the specified display.
|
||||
u32 FindBufferQueueId(u64 display_id, u64 layer_id) const;
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id);
|
||||
|
||||
/// Obtains a buffer queue identified by the id.
|
||||
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
|
||||
/// Obtains a buffer queue identified by the ID.
|
||||
std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const;
|
||||
|
||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||
/// finished.
|
||||
void Compose();
|
||||
|
||||
private:
|
||||
/// Returns the display identified by the specified id.
|
||||
Display& GetDisplay(u64 display_id);
|
||||
/// Finds the display identified by the specified ID.
|
||||
Display& FindDisplay(u64 display_id);
|
||||
|
||||
/// Returns the layer identified by the specified id in the desired display.
|
||||
Layer& GetLayer(u64 display_id, u64 layer_id);
|
||||
/// Finds the display identified by the specified ID.
|
||||
const Display& FindDisplay(u64 display_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
Layer& FindLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
const Layer& FindLayer(u64 display_id, u64 layer_id) const;
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
|
||||
std::vector<Display> displays;
|
||||
std::array<Display, 5> displays{{
|
||||
{0, "Default"},
|
||||
{1, "External"},
|
||||
{2, "Edid"},
|
||||
{3, "Internal"},
|
||||
{4, "Null"},
|
||||
}};
|
||||
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
|
||||
|
||||
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
explicit BootMode() : ServiceFramework{"pm:bm"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BootMode::GetBootMode, "GetBootMode"},
|
||||
{1, nullptr, "SetMaintenanceBoot"},
|
||||
{1, &BootMode::SetMaintenanceBoot, "SetMaintenanceBoot"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -24,8 +24,19 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode
|
||||
rb.PushEnum(boot_mode);
|
||||
}
|
||||
|
||||
void SetMaintenanceBoot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
|
||||
boot_mode = SystemBootMode::Maintenance;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
SystemBootMode boot_mode = SystemBootMode::Normal;
|
||||
};
|
||||
|
||||
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
|
||||
|
||||
@@ -9,7 +9,12 @@ class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::PM {
|
||||
enum class SystemBootMode : u32 { Normal = 0, Maintenance = 1 };
|
||||
|
||||
enum class SystemBootMode {
|
||||
Normal,
|
||||
Maintenance,
|
||||
};
|
||||
|
||||
/// Registers all PM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ public:
|
||||
explicit PSC_C() : ServiceFramework{"psc:c"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{6, nullptr, "Unknown7"},
|
||||
{0, nullptr, "Initialize"},
|
||||
{1, nullptr, "DispatchRequest"},
|
||||
{2, nullptr, "GetResult"},
|
||||
{3, nullptr, "GetState"},
|
||||
{4, nullptr, "Cancel"},
|
||||
{5, nullptr, "PrintModuleInformation"},
|
||||
{6, nullptr, "GetModuleInformation"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -39,7 +39,8 @@ public:
|
||||
{0, nullptr, "Initialize"},
|
||||
{1, nullptr, "GetRequest"},
|
||||
{2, nullptr, "Acknowledge"},
|
||||
{3, nullptr, "Unknown1"},
|
||||
{3, nullptr, "Finalize"},
|
||||
{4, nullptr, "AcknowledgeEx"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -524,7 +524,7 @@ private:
|
||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||
static_cast<u32>(transaction), flags);
|
||||
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -558,7 +558,7 @@ private:
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
|
||||
|
||||
@@ -628,7 +628,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
|
||||
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
const auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
|
||||
// TODO(Subv): Find out what this actually is.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -704,13 +704,14 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
// This function currently does nothing but return a success error code in
|
||||
// the vi library itself, so do the same thing, but log out the passed in values.
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1043,7 +1044,7 @@ private:
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
|
||||
|
||||
const u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
const u32 buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -1062,7 +1063,7 @@ private:
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
const u32 buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
|
||||
@@ -87,6 +87,7 @@ add_library(video_core STATIC
|
||||
shader/decode.cpp
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
surface.cpp
|
||||
surface.h
|
||||
textures/astc.cpp
|
||||
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
u32 subchannel; ///< Current subchannel
|
||||
u32 method_count; ///< Current method count
|
||||
u32 length_pending; ///< Large NI command length pending
|
||||
bool non_incrementing; ///< Current command’s NI flag
|
||||
bool non_incrementing; ///< Current command's NI flag
|
||||
};
|
||||
|
||||
DmaState dma_state{};
|
||||
|
||||
@@ -208,6 +208,8 @@ enum class UniformType : u64 {
|
||||
SignedShort = 3,
|
||||
Single = 4,
|
||||
Double = 5,
|
||||
Quad = 6,
|
||||
UnsignedQuad = 7,
|
||||
};
|
||||
|
||||
enum class StoreType : u64 {
|
||||
@@ -784,6 +786,12 @@ union Instruction {
|
||||
BitField<44, 2, u64> unknown;
|
||||
} st_l;
|
||||
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} ldg;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -124,9 +126,36 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
||||
// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
|
||||
// So the values you see in docs might be multiplied by 4.
|
||||
enum class BufferMethods {
|
||||
BindObject = 0,
|
||||
CountBufferMethods = 0x40,
|
||||
BindObject = 0x0,
|
||||
Nop = 0x2,
|
||||
SemaphoreAddressHigh = 0x4,
|
||||
SemaphoreAddressLow = 0x5,
|
||||
SemaphoreSequence = 0x6,
|
||||
SemaphoreTrigger = 0x7,
|
||||
NotifyIntr = 0x8,
|
||||
WrcacheFlush = 0x9,
|
||||
Unk28 = 0xA,
|
||||
Unk2c = 0xB,
|
||||
RefCnt = 0x14,
|
||||
SemaphoreAcquire = 0x1A,
|
||||
SemaphoreRelease = 0x1B,
|
||||
Unk70 = 0x1C,
|
||||
Unk74 = 0x1D,
|
||||
Unk78 = 0x1E,
|
||||
Unk7c = 0x1F,
|
||||
Yield = 0x20,
|
||||
NonPullerMethods = 0x40,
|
||||
};
|
||||
|
||||
enum class GpuSemaphoreOperation {
|
||||
AcquireEqual = 0x1,
|
||||
WriteLong = 0x2,
|
||||
AcquireGequal = 0x4,
|
||||
AcquireMask = 0x8,
|
||||
};
|
||||
|
||||
void GPU::CallMethod(const MethodCall& method_call) {
|
||||
@@ -135,20 +164,78 @@ void GPU::CallMethod(const MethodCall& method_call) {
|
||||
|
||||
ASSERT(method_call.subchannel < bound_engines.size());
|
||||
|
||||
if (method_call.method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
||||
method_call.argument);
|
||||
bound_engines[method_call.subchannel] = static_cast<EngineID>(method_call.argument);
|
||||
return;
|
||||
if (ExecuteMethodOnEngine(method_call)) {
|
||||
CallEngineMethod(method_call);
|
||||
} else {
|
||||
CallPullerMethod(method_call);
|
||||
}
|
||||
}
|
||||
|
||||
if (method_call.method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
|
||||
// TODO(Subv): Research and implement these methods.
|
||||
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
|
||||
return;
|
||||
bool GPU::ExecuteMethodOnEngine(const MethodCall& method_call) {
|
||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||
return method >= BufferMethods::NonPullerMethods;
|
||||
}
|
||||
|
||||
void GPU::CallPullerMethod(const MethodCall& method_call) {
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||
|
||||
switch (method) {
|
||||
case BufferMethods::BindObject: {
|
||||
ProcessBindMethod(method_call);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Nop:
|
||||
case BufferMethods::SemaphoreAddressHigh:
|
||||
case BufferMethods::SemaphoreAddressLow:
|
||||
case BufferMethods::SemaphoreSequence:
|
||||
case BufferMethods::RefCnt:
|
||||
break;
|
||||
case BufferMethods::SemaphoreTrigger: {
|
||||
ProcessSemaphoreTriggerMethod();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::NotifyIntr: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::WrcacheFlush: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method WrcacheFlush not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Unk28: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Unk2c: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Unk2c not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreAcquire: {
|
||||
ProcessSemaphoreAcquire();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreRelease: {
|
||||
ProcessSemaphoreRelease();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Yield: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented",
|
||||
static_cast<u32>(method));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::CallEngineMethod(const MethodCall& method_call) {
|
||||
const EngineID engine = bound_engines[method_call.subchannel];
|
||||
|
||||
switch (engine) {
|
||||
@@ -172,4 +259,76 @@ void GPU::CallMethod(const MethodCall& method_call) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::ProcessBindMethod(const MethodCall& method_call) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
||||
method_call.argument);
|
||||
bound_engines[method_call.subchannel] = static_cast<EngineID>(method_call.argument);
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreTriggerMethod() {
|
||||
const auto semaphoreOperationMask = 0xF;
|
||||
const auto op =
|
||||
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
|
||||
if (op == GpuSemaphoreOperation::WriteLong) {
|
||||
auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
struct Block {
|
||||
u32 sequence;
|
||||
u32 zeros = 0;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
Block block{};
|
||||
block.sequence = regs.semaphore_sequence;
|
||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||
// CoreTiming
|
||||
block.timestamp = CoreTiming::GetTicks();
|
||||
Memory::WriteBlock(*address, &block, sizeof(block));
|
||||
} else {
|
||||
const auto address =
|
||||
memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
const u32 word = Memory::Read32(*address);
|
||||
if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
|
||||
(op == GpuSemaphoreOperation::AcquireGequal &&
|
||||
static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
|
||||
(op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) {
|
||||
// Nothing to do in this case
|
||||
} else {
|
||||
regs.acquire_source = true;
|
||||
regs.acquire_value = regs.semaphore_sequence;
|
||||
if (op == GpuSemaphoreOperation::AcquireEqual) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = false;
|
||||
} else if (op == GpuSemaphoreOperation::AcquireGequal) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = true;
|
||||
} else if (op == GpuSemaphoreOperation::AcquireMask) {
|
||||
// TODO(kemathe) The acquire mask operation waits for a value that, ANDed with
|
||||
// semaphore_sequence, gives a non-0 result
|
||||
LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented");
|
||||
} else {
|
||||
LOG_ERROR(HW_GPU, "Invalid semaphore operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreRelease() {
|
||||
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
Memory::Write32(*address, regs.semaphore_release);
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreAcquire() {
|
||||
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
const u32 word = Memory::Read32(*address);
|
||||
const auto value = regs.semaphore_acquire;
|
||||
if (word != value) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_value = value;
|
||||
// TODO(kemathe73) figure out how to do the acquire_timeout
|
||||
regs.acquire_mode = false;
|
||||
regs.acquire_source = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -156,6 +156,46 @@ public:
|
||||
/// Returns a const reference to the GPU DMA pusher.
|
||||
const Tegra::DmaPusher& DmaPusher() const;
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x100;
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr SmaphoreAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} smaphore_address;
|
||||
|
||||
u32 semaphore_sequence;
|
||||
u32 semaphore_trigger;
|
||||
INSERT_PADDING_WORDS(0xC);
|
||||
|
||||
// The puser and the puller share the reference counter, the pusher only has read
|
||||
// access
|
||||
u32 reference_count;
|
||||
INSERT_PADDING_WORDS(0x5);
|
||||
|
||||
u32 semaphore_acquire;
|
||||
u32 semaphore_release;
|
||||
INSERT_PADDING_WORDS(0xE4);
|
||||
|
||||
// Puller state
|
||||
u32 acquire_mode;
|
||||
u32 acquire_source;
|
||||
u32 acquire_active;
|
||||
u32 acquire_timeout;
|
||||
u32 acquire_value;
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
@@ -173,6 +213,37 @@ private:
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
/// Inline memory engine
|
||||
std::unique_ptr<Engines::KeplerMemory> kepler_memory;
|
||||
|
||||
void ProcessBindMethod(const MethodCall& method_call);
|
||||
void ProcessSemaphoreTriggerMethod();
|
||||
void ProcessSemaphoreRelease();
|
||||
void ProcessSemaphoreAcquire();
|
||||
|
||||
// Calls a GPU puller method.
|
||||
void CallPullerMethod(const MethodCall& method_call);
|
||||
// Calls a GPU engine method.
|
||||
void CallEngineMethod(const MethodCall& method_call);
|
||||
// Determines where the method should be executed.
|
||||
bool ExecuteMethodOnEngine(const MethodCall& method_call);
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(GPU::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(smaphore_address, 0x4);
|
||||
ASSERT_REG_POSITION(semaphore_sequence, 0x6);
|
||||
ASSERT_REG_POSITION(semaphore_trigger, 0x7);
|
||||
ASSERT_REG_POSITION(reference_count, 0x14);
|
||||
ASSERT_REG_POSITION(semaphore_acquire, 0x1A);
|
||||
ASSERT_REG_POSITION(semaphore_release, 0x1B);
|
||||
|
||||
ASSERT_REG_POSITION(acquire_mode, 0x100);
|
||||
ASSERT_REG_POSITION(acquire_source, 0x101);
|
||||
ASSERT_REG_POSITION(acquire_active, 0x102);
|
||||
ASSERT_REG_POSITION(acquire_timeout, 0x103);
|
||||
ASSERT_REG_POSITION(acquire_value, 0x104);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -49,11 +49,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to fill a region
|
||||
virtual bool AccelerateFill(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to display the framebuffer to screen
|
||||
virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
|
||||
u32 pixel_stride) {
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@@ -18,7 +23,72 @@ CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{
|
||||
LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory");
|
||||
}
|
||||
|
||||
void CachedGlobalRegion::Reload(u32 size_) {
|
||||
constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize);
|
||||
|
||||
size = size_;
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_,
|
||||
max_size);
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const {
|
||||
const auto search{reserve.find(addr)};
|
||||
if (search == reserve.end()) {
|
||||
return {};
|
||||
}
|
||||
return search->second;
|
||||
}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) {
|
||||
GlobalRegion region{TryGetReservedGlobalRegion(addr, size)};
|
||||
if (!region) {
|
||||
// No reserved surface available, create a new one and reserve it
|
||||
region = std::make_shared<CachedGlobalRegion>(addr, size);
|
||||
ReserveGlobalRegion(region);
|
||||
}
|
||||
region->Reload(size);
|
||||
return region;
|
||||
}
|
||||
|
||||
void GlobalRegionCacheOpenGL::ReserveGlobalRegion(const GlobalRegion& region) {
|
||||
reserve[region->GetAddr()] = region;
|
||||
}
|
||||
|
||||
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
|
||||
: RasterizerCache{rasterizer} {}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
|
||||
const GLShader::GlobalMemoryEntry& global_region,
|
||||
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
|
||||
|
||||
auto& gpu{Core::System::GetInstance().GPU()};
|
||||
const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)];
|
||||
const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress(
|
||||
cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset());
|
||||
ASSERT(cbuf_addr);
|
||||
|
||||
const auto actual_addr_gpu = Memory::Read64(*cbuf_addr);
|
||||
const auto size = Memory::Read32(*cbuf_addr + 8);
|
||||
const auto actual_addr = gpu.MemoryManager().GpuToCpuAddress(actual_addr_gpu);
|
||||
ASSERT(actual_addr);
|
||||
|
||||
// Look up global region in the cache based on address
|
||||
GlobalRegion region = TryGet(*actual_addr);
|
||||
|
||||
if (!region) {
|
||||
// No global region found - create a new one
|
||||
region = GetUncachedGlobalRegion(*actual_addr, size);
|
||||
Register(region);
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
@@ -40,6 +44,9 @@ public:
|
||||
return buffer.handle;
|
||||
}
|
||||
|
||||
/// Reloads the global region from guest memory
|
||||
void Reload(u32 size_);
|
||||
|
||||
// TODO(Rodrigo): When global memory is written (STG), implement flushing
|
||||
void Flush() override {
|
||||
UNIMPLEMENTED();
|
||||
@@ -55,6 +62,17 @@ private:
|
||||
class GlobalRegionCacheOpenGL final : public RasterizerCache<GlobalRegion> {
|
||||
public:
|
||||
explicit GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer);
|
||||
|
||||
/// Gets the current specified shader stage program
|
||||
GlobalRegion GetGlobalRegion(const GLShader::GlobalMemoryEntry& descriptor,
|
||||
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
|
||||
|
||||
private:
|
||||
GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const;
|
||||
GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size);
|
||||
void ReserveGlobalRegion(const GlobalRegion& region);
|
||||
|
||||
std::unordered_map<VAddr, GlobalRegion> reserve;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -297,10 +297,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Shader);
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
|
||||
u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
|
||||
u32 current_texture_bindpoint = 0;
|
||||
BaseBindings base_bindings;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
@@ -324,43 +321,35 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
const GLintptr offset = buffer_cache.UploadHostMemory(
|
||||
&ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Bind the buffer
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
|
||||
offset, static_cast<GLsizeiptr>(sizeof(ubo)));
|
||||
// Bind the emulation info buffer
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset,
|
||||
static_cast<GLsizeiptr>(sizeof(ubo)));
|
||||
|
||||
Shader shader{shader_cache.GetStageProgram(program)};
|
||||
const auto [program_handle, next_bindings] =
|
||||
shader->GetProgramHandle(primitive_mode, base_bindings);
|
||||
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
case Maxwell::ShaderProgram::VertexB: {
|
||||
shader_program_manager->UseProgrammableVertexShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
case Maxwell::ShaderProgram::VertexB:
|
||||
shader_program_manager->UseProgrammableVertexShader(program_handle);
|
||||
break;
|
||||
}
|
||||
case Maxwell::ShaderProgram::Geometry: {
|
||||
shader_program_manager->UseProgrammableGeometryShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
shader_program_manager->UseProgrammableGeometryShader(program_handle);
|
||||
break;
|
||||
}
|
||||
case Maxwell::ShaderProgram::Fragment: {
|
||||
shader_program_manager->UseProgrammableFragmentShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
shader_program_manager->UseProgrammableFragmentShader(program_handle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
|
||||
shader_config.enable.Value(), shader_config.offset);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Configure the const buffers for this shader stage.
|
||||
current_constbuffer_bindpoint =
|
||||
SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
|
||||
current_constbuffer_bindpoint);
|
||||
|
||||
// Configure the textures for this shader stage.
|
||||
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
|
||||
primitive_mode, current_texture_bindpoint);
|
||||
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
|
||||
SetupConstBuffers(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupGlobalRegions(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupTextures(stage_enum, shader, program_handle, base_bindings);
|
||||
|
||||
// Workaround for Intel drivers.
|
||||
// When a clip distance is enabled but not set in the shader it crops parts of the screen
|
||||
@@ -375,6 +364,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
||||
index++;
|
||||
}
|
||||
|
||||
base_bindings = next_bindings;
|
||||
}
|
||||
|
||||
SyncClipEnabled(clip_distances);
|
||||
@@ -790,11 +781,6 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const void* config) {
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
VAddr framebuffer_addr, u32 pixel_stride) {
|
||||
if (!framebuffer_addr) {
|
||||
@@ -924,8 +910,9 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
}
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_bindpoint) {
|
||||
void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLuint program_handle,
|
||||
BaseBindings base_bindings) {
|
||||
MICROPROFILE_SCOPE(OpenGL_UBO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
@@ -973,75 +960,73 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
|
||||
size = Common::AlignUp(size, sizeof(GLvec4));
|
||||
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
|
||||
|
||||
GLintptr const_buffer_offset = buffer_cache.UploadMemory(
|
||||
const GLintptr const_buffer_offset = buffer_cache.UploadMemory(
|
||||
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Now configure the bindpoint of the buffer inside the shader
|
||||
glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetProgramResourceIndex(used_buffer),
|
||||
current_bindpoint + bindpoint);
|
||||
|
||||
// Prepare values for multibind
|
||||
bind_buffers[bindpoint] = buffer_cache.GetHandle();
|
||||
bind_offsets[bindpoint] = const_buffer_offset;
|
||||
bind_sizes[bindpoint] = size;
|
||||
}
|
||||
|
||||
glBindBuffersRange(GL_UNIFORM_BUFFER, current_bindpoint, static_cast<GLsizei>(entries.size()),
|
||||
// The first binding is reserved for emulation values
|
||||
const GLuint ubo_base_binding = base_bindings.cbuf + 1;
|
||||
glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()),
|
||||
bind_buffers.data(), bind_offsets.data(), bind_sizes.data());
|
||||
|
||||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_unit) {
|
||||
void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings) {
|
||||
// TODO(Rodrigo): Use ARB_multi_bind here
|
||||
const auto& entries = shader->GetShaderEntries().global_memory_entries;
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const u32 current_bindpoint = base_bindings.gmem + bindpoint;
|
||||
const auto& region = global_cache.GetGlobalRegion(entry, stage);
|
||||
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& entries = shader->GetShaderEntries().samplers;
|
||||
|
||||
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
|
||||
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.texture_units),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const u32 current_bindpoint = current_unit + bindpoint;
|
||||
|
||||
// Bind the uniform to the sampler.
|
||||
|
||||
glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetUniformLocation(entry), current_bindpoint);
|
||||
const u32 current_bindpoint = base_bindings.sampler + bindpoint;
|
||||
auto& unit = state.texture_units[current_bindpoint];
|
||||
|
||||
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
|
||||
|
||||
if (!texture.enabled) {
|
||||
state.texture_units[current_bindpoint].texture = 0;
|
||||
unit.texture = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
const GLuint handle =
|
||||
unit.texture =
|
||||
entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
|
||||
const GLenum target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
|
||||
state.texture_units[current_bindpoint].texture = handle;
|
||||
state.texture_units[current_bindpoint].target = target;
|
||||
state.texture_units[current_bindpoint].swizzle.r =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
state.texture_units[current_bindpoint].swizzle.g =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.y_source);
|
||||
state.texture_units[current_bindpoint].swizzle.b =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.z_source);
|
||||
state.texture_units[current_bindpoint].swizzle.a =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.w_source);
|
||||
unit.target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
|
||||
unit.swizzle.r = MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
unit.swizzle.g = MaxwellToGL::SwizzleSource(texture.tic.y_source);
|
||||
unit.swizzle.b = MaxwellToGL::SwizzleSource(texture.tic.z_source);
|
||||
unit.swizzle.a = MaxwellToGL::SwizzleSource(texture.tic.w_source);
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
state.texture_units[current_bindpoint].texture = 0;
|
||||
unit.texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return current_unit + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
|
||||
@@ -56,7 +56,6 @@ public:
|
||||
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
|
||||
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst) override;
|
||||
bool AccelerateFill(const void* config) override;
|
||||
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
|
||||
u32 pixel_stride) override;
|
||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||
@@ -127,25 +126,18 @@ private:
|
||||
bool using_depth_fb = true, bool preserve_contents = true,
|
||||
std::optional<std::size_t> single_color_target = {});
|
||||
|
||||
/**
|
||||
* Configures the current constbuffers to use for the draw command.
|
||||
* @param stage The shader stage to configure buffers for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
* @param current_bindpoint The offset at which to start counting new buffer bindpoints.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_bindpoint);
|
||||
/// Configures the current constbuffers to use for the draw command.
|
||||
void SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
|
||||
/**
|
||||
* Configures the current textures to use for the draw command.
|
||||
* @param stage The shader stage to configure textures for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
* @param current_unit The offset at which to start counting unused texture units.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_unit);
|
||||
/// Configures the current global memory entries to use for the draw command.
|
||||
void SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
|
||||
@@ -128,6 +128,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
||||
params.unaligned_height = config.tic.Height();
|
||||
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
|
||||
params.identity = SurfaceClass::Uploaded;
|
||||
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
@@ -167,6 +168,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
}
|
||||
|
||||
params.is_layered = SurfaceTargetIsLayered(params.target);
|
||||
params.is_array = SurfaceTargetIsArray(params.target);
|
||||
params.max_mip_level = config.tic.max_mip_level + 1;
|
||||
params.rt = {};
|
||||
|
||||
@@ -194,6 +196,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::RenderTarget;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.is_layered = false;
|
||||
@@ -229,6 +232,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = zeta_height;
|
||||
params.unaligned_height = zeta_height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::DepthBuffer;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.is_layered = false;
|
||||
@@ -257,6 +261,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::Copy;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.rt = {};
|
||||
@@ -574,8 +579,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
|
||||
|
||||
ApplyTextureDefaults(SurfaceTargetToGL(params.target), params.max_mip_level);
|
||||
|
||||
LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
|
||||
SurfaceParams::SurfaceTargetName(params.target));
|
||||
OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString());
|
||||
|
||||
// Clamp size to mapped GPU memory region
|
||||
// TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
|
||||
@@ -730,7 +734,6 @@ void CachedSurface::FlushGLBuffer() {
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width,
|
||||
params.height);
|
||||
ASSERT(params.type != SurfaceType::Fill);
|
||||
const u8* const texture_src_data = Memory::GetPointer(params.addr);
|
||||
ASSERT(texture_src_data);
|
||||
if (params.is_tiled) {
|
||||
@@ -877,10 +880,13 @@ void CachedSurface::EnsureTextureView() {
|
||||
UNIMPLEMENTED_IF(gl_is_compressed);
|
||||
|
||||
const GLenum target{TargetLayer()};
|
||||
const GLuint num_layers{target == GL_TEXTURE_CUBE_MAP_ARRAY ? 6u : 1u};
|
||||
constexpr GLuint min_layer = 0;
|
||||
constexpr GLuint min_level = 0;
|
||||
|
||||
texture_view.Create();
|
||||
glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, 0,
|
||||
params.max_mip_level, 0, 1);
|
||||
glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, min_level,
|
||||
params.max_mip_level, min_layer, num_layers);
|
||||
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
const auto& old_tex = cur_state.texture_units[0];
|
||||
@@ -897,9 +903,6 @@ void CachedSurface::EnsureTextureView() {
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
|
||||
if (params.type == SurfaceType::Fill)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_TextureUL);
|
||||
|
||||
for (u32 i = 0; i < params.max_mip_level; i++)
|
||||
|
||||
@@ -35,6 +35,14 @@ using PixelFormat = VideoCore::Surface::PixelFormat;
|
||||
using ComponentType = VideoCore::Surface::ComponentType;
|
||||
|
||||
struct SurfaceParams {
|
||||
|
||||
enum class SurfaceClass {
|
||||
Uploaded,
|
||||
RenderTarget,
|
||||
DepthBuffer,
|
||||
Copy,
|
||||
};
|
||||
|
||||
static std::string SurfaceTargetName(SurfaceTarget target) {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
@@ -210,6 +218,48 @@ struct SurfaceParams {
|
||||
/// Initializes parameters for caching, should be called after everything has been initialized
|
||||
void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
|
||||
|
||||
std::string TargetName() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
return "1D";
|
||||
case SurfaceTarget::Texture2D:
|
||||
return "2D";
|
||||
case SurfaceTarget::Texture3D:
|
||||
return "3D";
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
return "1DArray";
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
return "2DArray";
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return "Cube";
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
|
||||
UNREACHABLE();
|
||||
return fmt::format("TUK({})", static_cast<u32>(target));
|
||||
}
|
||||
}
|
||||
|
||||
std::string ClassName() const {
|
||||
switch (identity) {
|
||||
case SurfaceClass::Uploaded:
|
||||
return "UP";
|
||||
case SurfaceClass::RenderTarget:
|
||||
return "RT";
|
||||
case SurfaceClass::DepthBuffer:
|
||||
return "DB";
|
||||
case SurfaceClass::Copy:
|
||||
return "CP";
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented surface_class={}", static_cast<u32>(identity));
|
||||
UNREACHABLE();
|
||||
return fmt::format("CUK({})", static_cast<u32>(identity));
|
||||
}
|
||||
}
|
||||
|
||||
std::string IdentityString() const {
|
||||
return ClassName() + '_' + TargetName() + '_' + (is_tiled ? 'T' : 'L');
|
||||
}
|
||||
|
||||
bool is_tiled;
|
||||
u32 block_width;
|
||||
u32 block_height;
|
||||
@@ -223,8 +273,10 @@ struct SurfaceParams {
|
||||
u32 depth;
|
||||
u32 unaligned_height;
|
||||
SurfaceTarget target;
|
||||
SurfaceClass identity;
|
||||
u32 max_mip_level;
|
||||
bool is_layered;
|
||||
bool is_array;
|
||||
bool srgb_conversion;
|
||||
// Parameters used for caching
|
||||
VAddr addr;
|
||||
@@ -255,6 +307,7 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
|
||||
static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
|
||||
SurfaceReserveKey res;
|
||||
res.state = params;
|
||||
res.state.identity = {}; // Ignore the origin of the texture
|
||||
res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
|
||||
res.state.rt = {}; // Ignore rt config in caching
|
||||
return res;
|
||||
@@ -294,7 +347,7 @@ public:
|
||||
}
|
||||
|
||||
const OGLTexture& TextureLayer() {
|
||||
if (params.is_layered) {
|
||||
if (params.is_array) {
|
||||
return Texture();
|
||||
}
|
||||
EnsureTextureView();
|
||||
|
||||
@@ -34,36 +34,25 @@ static ProgramCode GetShaderCode(VAddr addr) {
|
||||
return program_code;
|
||||
}
|
||||
|
||||
/// Helper function to set shader uniform block bindings for a single shader stage
|
||||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
|
||||
Maxwell::ShaderStage binding, std::size_t expected_size) {
|
||||
const GLuint ub_index = glGetUniformBlockIndex(shader, name);
|
||||
if (ub_index == GL_INVALID_INDEX) {
|
||||
return;
|
||||
/// Gets the shader type from a Maxwell program type
|
||||
constexpr GLenum GetShaderType(Maxwell::ShaderProgram program_type) {
|
||||
switch (program_type) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
case Maxwell::ShaderProgram::VertexB:
|
||||
return GL_VERTEX_SHADER;
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
return GL_GEOMETRY_SHADER;
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
return GL_FRAGMENT_SHADER;
|
||||
default:
|
||||
return GL_NONE;
|
||||
}
|
||||
|
||||
GLint ub_size = 0;
|
||||
glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
|
||||
ASSERT_MSG(static_cast<std::size_t>(ub_size) == expected_size,
|
||||
"Uniform block size did not match! Got {}, expected {}", ub_size, expected_size);
|
||||
glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
|
||||
}
|
||||
|
||||
/// Sets shader uniform block bindings for an entire shader program
|
||||
static void SetShaderUniformBlockBindings(GLuint shader) {
|
||||
SetShaderUniformBlockBinding(shader, "vs_config", Maxwell::ShaderStage::Vertex,
|
||||
sizeof(GLShader::MaxwellUniformData));
|
||||
SetShaderUniformBlockBinding(shader, "gs_config", Maxwell::ShaderStage::Geometry,
|
||||
sizeof(GLShader::MaxwellUniformData));
|
||||
SetShaderUniformBlockBinding(shader, "fs_config", Maxwell::ShaderStage::Fragment,
|
||||
sizeof(GLShader::MaxwellUniformData));
|
||||
}
|
||||
|
||||
CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
: addr{addr}, program_type{program_type}, setup{GetShaderCode(addr)} {
|
||||
|
||||
GLShader::ProgramResult program_result;
|
||||
GLenum gl_type{};
|
||||
|
||||
switch (program_type) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
@@ -74,17 +63,14 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
case Maxwell::ShaderProgram::VertexB:
|
||||
CalculateProperties();
|
||||
program_result = GLShader::GenerateVertexShader(setup);
|
||||
gl_type = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
CalculateProperties();
|
||||
program_result = GLShader::GenerateGeometryShader(setup);
|
||||
gl_type = GL_GEOMETRY_SHADER;
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
CalculateProperties();
|
||||
program_result = GLShader::GenerateFragmentShader(setup);
|
||||
gl_type = GL_FRAGMENT_SHADER;
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type));
|
||||
@@ -92,59 +78,105 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
return;
|
||||
}
|
||||
|
||||
code = program_result.first;
|
||||
entries = program_result.second;
|
||||
shader_length = entries.shader_length;
|
||||
}
|
||||
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
OGLShader shader;
|
||||
shader.Create(program_result.first.c_str(), gl_type);
|
||||
program.Create(true, shader.handle);
|
||||
SetShaderUniformBlockBindings(program.handle);
|
||||
LabelGLObject(GL_PROGRAM, program.handle, addr);
|
||||
std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode,
|
||||
BaseBindings base_bindings) {
|
||||
GLuint handle{};
|
||||
if (program_type == Maxwell::ShaderProgram::Geometry) {
|
||||
handle = GetGeometryShader(primitive_mode, base_bindings);
|
||||
} else {
|
||||
// Store shader's code to lazily build it on draw
|
||||
geometry_programs.code = program_result.first;
|
||||
const auto [entry, is_cache_miss] = programs.try_emplace(base_bindings);
|
||||
auto& program = entry->second;
|
||||
if (is_cache_miss) {
|
||||
std::string source = AllocateBindings(base_bindings);
|
||||
source += code;
|
||||
|
||||
OGLShader shader;
|
||||
shader.Create(source.c_str(), GetShaderType(program_type));
|
||||
program.Create(true, shader.handle);
|
||||
LabelGLObject(GL_PROGRAM, program.handle, addr);
|
||||
}
|
||||
|
||||
handle = program.handle;
|
||||
}
|
||||
|
||||
// Add const buffer and samplers offset reserved by this shader. One UBO binding is reserved for
|
||||
// emulation values
|
||||
base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size()) + 1;
|
||||
base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size());
|
||||
base_bindings.sampler += static_cast<u32>(entries.samplers.size());
|
||||
|
||||
return {handle, base_bindings};
|
||||
}
|
||||
|
||||
std::string CachedShader::AllocateBindings(BaseBindings base_bindings) {
|
||||
std::string code = "#version 430 core\n";
|
||||
code += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++);
|
||||
|
||||
for (const auto& cbuf : entries.const_buffers) {
|
||||
code += fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++);
|
||||
}
|
||||
|
||||
for (const auto& gmem : entries.global_memory_entries) {
|
||||
code += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(),
|
||||
gmem.GetCbufOffset(), base_bindings.gmem++);
|
||||
}
|
||||
|
||||
for (const auto& sampler : entries.samplers) {
|
||||
code += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(),
|
||||
base_bindings.sampler++);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) {
|
||||
const auto [entry, is_cache_miss] = geometry_programs.try_emplace(base_bindings);
|
||||
auto& programs = entry->second;
|
||||
|
||||
switch (primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints");
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return LazyGeometryProgram(programs.lines, base_bindings, "lines", 2, "ShaderLines");
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(programs.lines_adjacency, base_bindings, "lines_adjacency", 4,
|
||||
"ShaderLinesAdjacency");
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return LazyGeometryProgram(programs.triangles, base_bindings, "triangles", 3,
|
||||
"ShaderTriangles");
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(programs.triangles_adjacency, base_bindings,
|
||||
"triangles_adjacency", 6, "ShaderTrianglesAdjacency");
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints");
|
||||
}
|
||||
}
|
||||
|
||||
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
|
||||
const auto search{resource_cache.find(buffer.GetHash())};
|
||||
if (search == resource_cache.end()) {
|
||||
const GLuint index{
|
||||
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())};
|
||||
resource_cache[buffer.GetHash()] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
return search->second;
|
||||
}
|
||||
|
||||
GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
|
||||
const auto search{uniform_cache.find(sampler.GetHash())};
|
||||
if (search == uniform_cache.end()) {
|
||||
const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())};
|
||||
uniform_cache[sampler.GetHash()] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
return search->second;
|
||||
}
|
||||
|
||||
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
|
||||
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings,
|
||||
const std::string& glsl_topology, u32 max_vertices,
|
||||
const std::string& debug_name) {
|
||||
if (target_program.handle != 0) {
|
||||
return target_program.handle;
|
||||
}
|
||||
std::string source = "#version 430 core\n";
|
||||
std::string source = AllocateBindings(base_bindings);
|
||||
source += "layout (" + glsl_topology + ") in;\n";
|
||||
source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n';
|
||||
source += geometry_programs.code;
|
||||
source += code;
|
||||
|
||||
OGLShader shader;
|
||||
shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
|
||||
target_program.Create(true, shader.handle);
|
||||
SetShaderUniformBlockBindings(target_program.handle);
|
||||
LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
|
||||
return target_program.handle;
|
||||
};
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -23,6 +26,16 @@ class RasterizerOpenGL;
|
||||
using Shader = std::shared_ptr<CachedShader>;
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct BaseBindings {
|
||||
u32 cbuf{};
|
||||
u32 gmem{};
|
||||
u32 sampler{};
|
||||
|
||||
bool operator<(const BaseBindings& rhs) const {
|
||||
return std::tie(cbuf, gmem, sampler) < std::tie(rhs.cbuf, rhs.gmem, rhs.sampler);
|
||||
}
|
||||
};
|
||||
|
||||
class CachedShader final : public RasterizerCacheObject {
|
||||
public:
|
||||
CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
|
||||
@@ -44,70 +57,45 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the GL program handle for the shader
|
||||
GLuint GetProgramHandle(GLenum primitive_mode) {
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
return program.handle;
|
||||
}
|
||||
switch (primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return LazyGeometryProgram(geometry_programs.lines, "lines", 2, "ShaderLines");
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 4,
|
||||
"ShaderLinesAdjacency");
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return LazyGeometryProgram(geometry_programs.triangles, "triangles", 3,
|
||||
"ShaderTriangles");
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
|
||||
6, "ShaderTrianglesAdjacency");
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the GL program resource location for the specified resource, caching as needed
|
||||
GLuint GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer);
|
||||
|
||||
/// Gets the GL uniform location for the specified resource, caching as needed
|
||||
GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
|
||||
std::tuple<GLuint, BaseBindings> GetProgramHandle(GLenum primitive_mode,
|
||||
BaseBindings base_bindings);
|
||||
|
||||
private:
|
||||
/// Generates a geometry shader or returns one that already exists.
|
||||
GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
|
||||
u32 max_vertices, const std::string& debug_name);
|
||||
|
||||
void CalculateProperties();
|
||||
|
||||
VAddr addr;
|
||||
std::size_t shader_length;
|
||||
Maxwell::ShaderProgram program_type;
|
||||
GLShader::ShaderSetup setup;
|
||||
GLShader::ShaderEntries entries;
|
||||
|
||||
// Non-geometry program.
|
||||
OGLProgram program;
|
||||
|
||||
// Geometry programs. These are needed because GLSL needs an input topology but it's not
|
||||
// declared by the hardware. Workaround this issue by generating a different shader per input
|
||||
// topology class.
|
||||
struct {
|
||||
std::string code;
|
||||
struct GeometryPrograms {
|
||||
OGLProgram points;
|
||||
OGLProgram lines;
|
||||
OGLProgram lines_adjacency;
|
||||
OGLProgram triangles;
|
||||
OGLProgram triangles_adjacency;
|
||||
} geometry_programs;
|
||||
};
|
||||
|
||||
std::map<u32, GLuint> resource_cache;
|
||||
std::string AllocateBindings(BaseBindings base_bindings);
|
||||
|
||||
GLuint GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings);
|
||||
|
||||
/// Generates a geometry shader or returns one that already exists.
|
||||
GLuint LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings,
|
||||
const std::string& glsl_topology, u32 max_vertices,
|
||||
const std::string& debug_name);
|
||||
|
||||
void CalculateProperties();
|
||||
|
||||
VAddr addr{};
|
||||
std::size_t shader_length{};
|
||||
Maxwell::ShaderProgram program_type{};
|
||||
GLShader::ShaderSetup setup;
|
||||
GLShader::ShaderEntries entries;
|
||||
|
||||
std::string code;
|
||||
|
||||
std::map<BaseBindings, OGLProgram> programs;
|
||||
std::map<BaseBindings, GeometryPrograms> geometry_programs;
|
||||
|
||||
std::map<u32, GLuint> cbuf_resource_cache;
|
||||
std::map<u32, GLuint> gmem_resource_cache;
|
||||
std::map<u32, GLint> uniform_cache;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ using Operation = const OperationNode&;
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
||||
static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
|
||||
constexpr u32 MAX_GLOBALMEMORY_ELEMENTS =
|
||||
static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize) / sizeof(float);
|
||||
|
||||
enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
|
||||
|
||||
@@ -143,6 +145,7 @@ public:
|
||||
DeclareInputAttributes();
|
||||
DeclareOutputAttributes();
|
||||
DeclareConstantBuffers();
|
||||
DeclareGlobalMemory();
|
||||
DeclareSamplers();
|
||||
|
||||
code.AddLine("void execute_" + suffix + "() {");
|
||||
@@ -190,12 +193,15 @@ public:
|
||||
ShaderEntries GetShaderEntries() const {
|
||||
ShaderEntries entries;
|
||||
for (const auto& cbuf : ir.GetConstantBuffers()) {
|
||||
ConstBufferEntry desc(cbuf.second, stage, GetConstBufferBlock(cbuf.first), cbuf.first);
|
||||
entries.const_buffers.push_back(desc);
|
||||
entries.const_buffers.emplace_back(cbuf.second, stage, GetConstBufferBlock(cbuf.first),
|
||||
cbuf.first);
|
||||
}
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
SamplerEntry desc(sampler, stage, GetSampler(sampler));
|
||||
entries.samplers.push_back(desc);
|
||||
entries.samplers.emplace_back(sampler, stage, GetSampler(sampler));
|
||||
}
|
||||
for (const auto& gmem : ir.GetGlobalMemoryBases()) {
|
||||
entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset, stage,
|
||||
GetGlobalMemoryBlock(gmem));
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
entries.shader_length = ir.GetLength();
|
||||
@@ -368,13 +374,26 @@ private:
|
||||
void DeclareConstantBuffers() {
|
||||
for (const auto& entry : ir.GetConstantBuffers()) {
|
||||
const auto [index, size] = entry;
|
||||
code.AddLine("layout (std140) uniform " + GetConstBufferBlock(index) + " {");
|
||||
code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) +
|
||||
") uniform " + GetConstBufferBlock(index) + " {");
|
||||
code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];");
|
||||
code.AddLine("};");
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareGlobalMemory() {
|
||||
for (const auto& entry : ir.GetGlobalMemoryBases()) {
|
||||
const std::string binding =
|
||||
fmt::format("GMEM_BINDING_{}_{}", entry.cbuf_index, entry.cbuf_offset);
|
||||
code.AddLine("layout (std430, binding = " + binding + ") buffer " +
|
||||
GetGlobalMemoryBlock(entry) + " {");
|
||||
code.AddLine(" float " + GetGlobalMemory(entry) + "[MAX_GLOBALMEMORY_ELEMENTS];");
|
||||
code.AddLine("};");
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareSamplers() {
|
||||
const auto& samplers = ir.GetSamplers();
|
||||
for (const auto& sampler : samplers) {
|
||||
@@ -398,7 +417,8 @@ private:
|
||||
if (sampler.IsShadow())
|
||||
sampler_type += "Shadow";
|
||||
|
||||
code.AddLine("uniform " + sampler_type + ' ' + GetSampler(sampler) + ';');
|
||||
code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) +
|
||||
") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';');
|
||||
}
|
||||
if (!samplers.empty())
|
||||
code.AddNewLine();
|
||||
@@ -538,6 +558,12 @@ private:
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(node)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
|
||||
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
||||
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(node)) {
|
||||
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
|
||||
@@ -1471,6 +1497,15 @@ private:
|
||||
return GetDeclarationWithSuffix(index, "cbuf");
|
||||
}
|
||||
|
||||
std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const {
|
||||
return fmt::format("gmem_{}_{}_{}", descriptor.cbuf_index, descriptor.cbuf_offset, suffix);
|
||||
}
|
||||
|
||||
std::string GetGlobalMemoryBlock(const GlobalMemoryBase& descriptor) const {
|
||||
return fmt::format("gmem_block_{}_{}_{}", descriptor.cbuf_index, descriptor.cbuf_offset,
|
||||
suffix);
|
||||
}
|
||||
|
||||
std::string GetConstBufferBlock(u32 index) const {
|
||||
return GetDeclarationWithSuffix(index, "cbuf_block");
|
||||
}
|
||||
@@ -1505,8 +1540,10 @@ private:
|
||||
};
|
||||
|
||||
std::string GetCommonDeclarations() {
|
||||
return "#define MAX_CONSTBUFFER_ELEMENTS " + std::to_string(MAX_CONSTBUFFER_ELEMENTS) +
|
||||
"\n"
|
||||
const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS);
|
||||
const auto gmem = std::to_string(MAX_GLOBALMEMORY_ELEMENTS);
|
||||
return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" +
|
||||
"#define MAX_GLOBALMEMORY_ELEMENTS " + gmem + "\n" +
|
||||
"#define ftoi floatBitsToInt\n"
|
||||
"#define ftou floatBitsToUint\n"
|
||||
"#define itof intBitsToFloat\n"
|
||||
|
||||
@@ -38,10 +38,6 @@ public:
|
||||
return index;
|
||||
}
|
||||
|
||||
u32 GetHash() const {
|
||||
return (static_cast<u32>(stage) << 16) | index;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
Maxwell::ShaderStage stage{};
|
||||
@@ -62,18 +58,44 @@ public:
|
||||
return stage;
|
||||
}
|
||||
|
||||
u32 GetHash() const {
|
||||
return (static_cast<u32>(stage) << 16) | static_cast<u32>(GetIndex());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
Maxwell::ShaderStage stage{};
|
||||
};
|
||||
|
||||
class GlobalMemoryEntry {
|
||||
public:
|
||||
explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, Maxwell::ShaderStage stage,
|
||||
std::string name)
|
||||
: cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, stage{stage}, name{std::move(name)} {}
|
||||
|
||||
u32 GetCbufIndex() const {
|
||||
return cbuf_index;
|
||||
}
|
||||
|
||||
u32 GetCbufOffset() const {
|
||||
return cbuf_offset;
|
||||
}
|
||||
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
Maxwell::ShaderStage GetStage() const {
|
||||
return stage;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
Maxwell::ShaderStage stage{};
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffers;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::vector<GlobalMemoryEntry> global_memory_entries;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
std::size_t shader_length{};
|
||||
};
|
||||
|
||||
@@ -20,15 +20,14 @@ static constexpr u32 PROGRAM_OFFSET{10};
|
||||
ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#version 430 core\n";
|
||||
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += "// Shader Unique Id: VS" + id + "\n\n";
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout(std140) uniform vs_config {
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
@@ -78,7 +77,6 @@ void main() {
|
||||
}
|
||||
|
||||
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
|
||||
// Version is intentionally skipped in shader generation, it's added by the lazy compilation.
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
@@ -89,7 +87,7 @@ ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
|
||||
layout (location = 0) in vec4 gs_position[];
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140) uniform gs_config {
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
@@ -112,8 +110,7 @@ void main() {
|
||||
ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
|
||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||
|
||||
std::string out = "#version 430 core\n";
|
||||
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += "// Shader Unique Id: FS" + id + "\n\n";
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
@@ -129,7 +126,7 @@ layout (location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (location = 0) in vec4 position;
|
||||
|
||||
layout (std140) uniform fs_config {
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
|
||||
@@ -126,7 +126,7 @@ BasicBlock ShaderIR::DecodeRange(u32 begin, u32 end) {
|
||||
for (u32 pc = begin; pc < (begin > end ? MAX_PROGRAM_LENGTH : end);) {
|
||||
pc = DecodeInstr(basic_block, pc);
|
||||
}
|
||||
return std::move(basic_block);
|
||||
return basic_block;
|
||||
}
|
||||
|
||||
u32 ShaderIR::DecodeInstr(BasicBlock& bb, u32 pc) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -119,6 +120,54 @@ u32 ShaderIR::DecodeMemory(BasicBlock& bb, const BasicBlock& code, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::LDG: {
|
||||
const u32 count = [&]() {
|
||||
switch (instr.ldg.type) {
|
||||
case Tegra::Shader::UniformType::Single:
|
||||
return 1;
|
||||
case Tegra::Shader::UniformType::Double:
|
||||
return 2;
|
||||
case Tegra::Shader::UniformType::Quad:
|
||||
case Tegra::Shader::UniformType::UnsignedQuad:
|
||||
return 4;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented LDG size!");
|
||||
return 1;
|
||||
}
|
||||
}();
|
||||
|
||||
const Node addr_register = GetRegister(instr.gpr8);
|
||||
const Node base_address = TrackCbuf(addr_register, code, static_cast<s64>(code.size()));
|
||||
const auto cbuf = std::get_if<CbufNode>(base_address);
|
||||
ASSERT(cbuf != nullptr);
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
|
||||
ASSERT(cbuf_offset_imm != nullptr);
|
||||
const auto cbuf_offset = cbuf_offset_imm->GetValue() * 4;
|
||||
|
||||
bb.push_back(Comment(
|
||||
fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset)));
|
||||
|
||||
const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset};
|
||||
used_global_memory_bases.insert(descriptor);
|
||||
|
||||
const Node immediate_offset =
|
||||
Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value()));
|
||||
const Node base_real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register);
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
|
||||
SetTemporal(bb, i, gmem);
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST_A: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
|
||||
"Indirect attribute loads are not supported");
|
||||
|
||||
@@ -257,6 +257,15 @@ private:
|
||||
bool is_indirect{};
|
||||
};
|
||||
|
||||
struct GlobalMemoryBase {
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
|
||||
bool operator<(const GlobalMemoryBase& rhs) const {
|
||||
return std::tie(cbuf_index, cbuf_offset) < std::tie(rhs.cbuf_index, rhs.cbuf_offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct MetaArithmetic {
|
||||
bool precise{};
|
||||
};
|
||||
@@ -391,7 +400,7 @@ private:
|
||||
const InternalFlag flag;
|
||||
};
|
||||
|
||||
/// A predicate register, it can be negated without aditional nodes
|
||||
/// A predicate register, it can be negated without additional nodes
|
||||
class PredicateNode final {
|
||||
public:
|
||||
explicit constexpr PredicateNode(Tegra::Shader::Pred index, bool negated)
|
||||
@@ -415,11 +424,11 @@ class AbufNode final {
|
||||
public:
|
||||
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
|
||||
const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
|
||||
: input_mode{input_mode}, index{index}, element{element}, buffer{buffer} {}
|
||||
: input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
|
||||
|
||||
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
|
||||
Node buffer = {})
|
||||
: input_mode{}, index{index}, element{element}, buffer{buffer} {}
|
||||
: input_mode{}, buffer{buffer}, index{index}, element{element} {}
|
||||
|
||||
Tegra::Shader::IpaMode GetInputMode() const {
|
||||
return input_mode;
|
||||
@@ -478,14 +487,26 @@ private:
|
||||
/// Global memory node
|
||||
class GmemNode final {
|
||||
public:
|
||||
explicit constexpr GmemNode(Node address) : address{address} {}
|
||||
explicit constexpr GmemNode(Node real_address, Node base_address,
|
||||
const GlobalMemoryBase& descriptor)
|
||||
: real_address{real_address}, base_address{base_address}, descriptor{descriptor} {}
|
||||
|
||||
Node GetAddress() const {
|
||||
return address;
|
||||
Node GetRealAddress() const {
|
||||
return real_address;
|
||||
}
|
||||
|
||||
Node GetBaseAddress() const {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
const GlobalMemoryBase& GetDescriptor() const {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
const Node address;
|
||||
const Node real_address;
|
||||
const Node base_address;
|
||||
const GlobalMemoryBase descriptor;
|
||||
};
|
||||
|
||||
/// Commentary, can be dropped
|
||||
@@ -543,6 +564,10 @@ public:
|
||||
return used_clip_distances;
|
||||
}
|
||||
|
||||
const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const {
|
||||
return used_global_memory_bases;
|
||||
}
|
||||
|
||||
std::size_t GetLength() const {
|
||||
return static_cast<std::size_t>(coverage_end * sizeof(u64));
|
||||
}
|
||||
@@ -626,10 +651,10 @@ private:
|
||||
Node GetPredicate(u64 pred, bool negated = false);
|
||||
/// Generates a predicate node for an immediate true or false value
|
||||
Node GetPredicate(bool immediate);
|
||||
/// Generates a node representing an input atttribute. Keeps track of used attributes.
|
||||
/// Generates a node representing an input attribute. Keeps track of used attributes.
|
||||
Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element,
|
||||
const Tegra::Shader::IpaMode& input_mode, Node buffer = {});
|
||||
/// Generates a node representing an output atttribute. Keeps track of used attributes.
|
||||
/// Generates a node representing an output attribute. Keeps track of used attributes.
|
||||
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
|
||||
/// Generates a node representing an internal flag
|
||||
Node GetInternalFlag(InternalFlag flag, bool negated = false);
|
||||
@@ -734,6 +759,10 @@ private:
|
||||
void WriteLop3Instruction(BasicBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b,
|
||||
Node op_c, Node imm_lut, bool sets_cc);
|
||||
|
||||
Node TrackCbuf(Node tracked, const BasicBlock& code, s64 cursor);
|
||||
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const BasicBlock& code, s64 cursor);
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, const T*... operands) {
|
||||
return StoreNode(OperationNode(code, operands...));
|
||||
@@ -786,6 +815,7 @@ private:
|
||||
std::map<u32, ConstBuffer> used_cbufs;
|
||||
std::set<Sampler> used_samplers;
|
||||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
|
||||
std::set<GlobalMemoryBase> used_global_memory_bases;
|
||||
|
||||
Tegra::Shader::Header header;
|
||||
};
|
||||
|
||||
76
src/video_core/shader/track.cpp
Normal file
76
src/video_core/shader/track.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
namespace {
|
||||
std::pair<Node, s64> FindOperation(const BasicBlock& code, s64 cursor,
|
||||
OperationCode operation_code) {
|
||||
for (; cursor >= 0; --cursor) {
|
||||
const Node node = code[cursor];
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
if (operation->GetCode() == operation_code)
|
||||
return {node, cursor};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Node ShaderIR::TrackCbuf(Node tracked, const BasicBlock& code, s64 cursor) {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
|
||||
// Cbuf found, but it has to be immediate
|
||||
return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
|
||||
}
|
||||
if (const auto gpr = std::get_if<GprNode>(tracked)) {
|
||||
if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
// Reduce the cursor in one to avoid infinite loops when the instruction sets the same
|
||||
// register that it uses as operand
|
||||
const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1);
|
||||
if (!source) {
|
||||
return nullptr;
|
||||
}
|
||||
return TrackCbuf(source, code, new_cursor);
|
||||
}
|
||||
if (const auto operation = std::get_if<OperationNode>(tracked)) {
|
||||
for (std::size_t i = 0; i < operation->GetOperandsCount(); ++i) {
|
||||
if (const auto found = TrackCbuf((*operation)[i], code, cursor)) {
|
||||
// Cbuf found in operand
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const BasicBlock& code,
|
||||
s64 cursor) {
|
||||
for (; cursor >= 0; --cursor) {
|
||||
const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign);
|
||||
if (!found_node) {
|
||||
return {};
|
||||
}
|
||||
const auto operation = std::get_if<OperationNode>(found_node);
|
||||
ASSERT(operation);
|
||||
|
||||
const auto& target = (*operation)[0];
|
||||
if (const auto gpr_target = std::get_if<GprNode>(target)) {
|
||||
if (gpr_target->GetIndex() == tracked->GetIndex()) {
|
||||
return {(*operation)[1], new_cursor};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
@@ -50,6 +50,24 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SurfaceTargetIsArray(SurfaceTarget target) {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return false;
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
return true;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::DepthFormat::S8_Z24_UNORM:
|
||||
|
||||
@@ -109,8 +109,7 @@ enum class SurfaceType {
|
||||
ColorTexture = 0,
|
||||
Depth = 1,
|
||||
DepthStencil = 2,
|
||||
Fill = 3,
|
||||
Invalid = 4,
|
||||
Invalid = 3,
|
||||
};
|
||||
|
||||
enum class SurfaceTarget {
|
||||
@@ -441,6 +440,8 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
|
||||
|
||||
bool SurfaceTargetIsLayered(SurfaceTarget target);
|
||||
|
||||
bool SurfaceTargetIsArray(SurfaceTarget target);
|
||||
|
||||
PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
|
||||
|
||||
PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
|
||||
|
||||
@@ -16,9 +16,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
||||
}
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||
return !Settings::values.resolution_factor
|
||||
? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
|
||||
: Settings::values.resolution_factor;
|
||||
return static_cast<u16>(
|
||||
Settings::values.resolution_factor
|
||||
? Settings::values.resolution_factor
|
||||
: renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
Reference in New Issue
Block a user