diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp index 5f3bbb7d..6b25196e 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp @@ -691,6 +691,12 @@ namespace skyline::soc::gm20b::engine::maxwell3d { #undef MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE } + void Maxwell3D::CallMethodBatchNonInc(u32 method, span arguments) { + for (u32 argument : arguments) { + CallMethod(method, argument); + } + } + void Maxwell3D::WriteSemaphoreResult(u64 result) { struct FourWordResult { u64 value; diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h index e3b4a125..226e8213 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h @@ -329,6 +329,8 @@ namespace skyline::soc::gm20b::engine::maxwell3d { void CallMethod(u32 method, u32 argument); + void CallMethodBatchNonInc(u32 method, span arguments); + void CallMethodFromMacro(u32 method, u32 argument) override; u32 ReadMethodFromMacro(u32 method) override; diff --git a/app/src/main/cpp/skyline/soc/gm20b/gpfifo.cpp b/app/src/main/cpp/skyline/soc/gm20b/gpfifo.cpp index 76b30039..1c327b98 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/gpfifo.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/gpfifo.cpp @@ -118,6 +118,17 @@ namespace skyline::soc::gm20b { } } + void ChannelGpfifo::SendPureBatchNonInc(u32 method, span arguments, SubchannelId subChannel) { + switch (subChannel) { + case SubchannelId::ThreeD: + channelCtx.maxwell3D->CallMethodBatchNonInc(method, arguments); + break; + default: + Logger::Warn("Called method 0x{:X} in unimplemented engine 0x{:X} with batch args", method, subChannel); + break; + } + } + void ChannelGpfifo::Process(GpEntry gpEntry) { if (!gpEntry.size) { // This is a GPFIFO control entry, all control entries have a zero length and contain no pushbuffers @@ -164,6 +175,9 @@ namespace skyline::soc::gm20b { // Process more methods if the entries are still not all used up after handling resuming for (; entry != pushBufferData.end(); entry++) { + if (entry >= pushBufferData.end()) + throw exception("GPFIFO buffer overflow!"); // This should never happen + // An entry containing all zeroes is a NOP, skip over it if (*entry == 0) continue; @@ -207,8 +221,38 @@ namespace skyline::soc::gm20b { } }}; + constexpr u32 BatchCutoff{4}; //!< Cutoff needed to send method calls in a batch which is espcially important for UBO updates. This helps to avoid the extra overhead batching for small packets. + // TODO: Only batch for specific target methods like UBO updates, since normal dispatch is generally cheaper + if (remainingEntries >= methodHeader.methodCount) { if (methodHeader.Pure()) [[likely]] { + if constexpr (State == MethodResumeState::State::NonInc) { + // For pure noninc methods we can send all method calls as a span in one go + if (methodHeader.methodCount > BatchCutoff) { + if constexpr (ThreeDOnly) + channelCtx.maxwell3D->CallMethodBatchNonInc(methodHeader.methodAddress, span(&(*++entry), methodHeader.methodCount)); + else + SendPureBatchNonInc(methodHeader.methodAddress, span(&(*++entry), methodHeader.methodCount), methodHeader.methodSubChannel); + + entry += methodHeader.methodCount - 1; + return false; + } + } else if constexpr (State == MethodResumeState::State::OneInc) { + // For pure oneinc methods we can send the initial method then send the rest as a span in one go + if (methodHeader.methodCount > (BatchCutoff + 1)) { + if constexpr (ThreeDOnly) { + channelCtx.maxwell3D->CallMethod(methodHeader.methodAddress, *++entry); + channelCtx.maxwell3D->CallMethodBatchNonInc(methodHeader.methodAddress + 1, span(&(*++entry), methodHeader.methodCount - 1)); + } else { + SendPure(methodHeader.methodAddress, *++entry, methodHeader.methodSubChannel); + SendPureBatchNonInc(methodHeader.methodAddress + 1, span(&(*++entry) ,methodHeader.methodCount - 1), methodHeader.methodSubChannel); + } + + entry += methodHeader.methodCount - 2; + return false; + } + } + for (u32 i{}; i < methodHeader.methodCount; i++) { if constexpr (ThreeDOnly) { channelCtx.maxwell3D->CallMethod(methodHeader.methodAddress + methodOffset(i), *++entry); diff --git a/app/src/main/cpp/skyline/soc/gm20b/gpfifo.h b/app/src/main/cpp/skyline/soc/gm20b/gpfifo.h index fdaf180f..1eb53201 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/gpfifo.h +++ b/app/src/main/cpp/skyline/soc/gm20b/gpfifo.h @@ -139,6 +139,11 @@ namespace skyline::soc::gm20b { */ void SendPure(u32 method, u32 argument, SubchannelId subchannel); + /** + * @brief Sends a batch of method calls all directed at the same method to the appropriate subchannel, macro and GPFIFO methods are not handled + */ + void SendPureBatchNonInc(u32 method, span arguments, SubchannelId subChannel); + /** * @brief Processes the pushbuffer contained within the given GpEntry, calling methods as needed */