mirror of
https://github.com/Takiiiiiiii/strato.git
synced 2025-07-17 08:46:39 +00:00
Implement the Host1x command FIFO together with barebones Host1x classes
The Host1x block of the TX1 supports 14 separate channels to which commands can be issued, these all run asynchronously so are emulated the same way as GPU channels with one FIFO emulation thread each. The command FIFO itself is very similar to the GPFIFO found in the GPU however there are some differences, mainly the introduction of classes (similar to engines) and the Mask opcode (which allows writing to a specific set of offsets much more efficiently). There is an internal Host1x class which functions similar to the GPFIFO class in the GPU, handling general operations such as syncpoint waits, this is accessed via the simple method interface. Other channels such as NVDEC and VIC are behind the 'Tegra Host Interface' (THI) in HW, this abstracts out the classes internal details and provides a uniform method interface ontop of the Host1x method one. We emulate the THI as a templated wrapper for the underlying class. Syncpoint increments in Host1x are different to GPU, the THI allows submitting increment requests that will be queued up and only be applied after a specific condition in the associated engine is met; however the option to for immediate increments is also available.
This commit is contained in:
147
app/src/main/cpp/skyline/soc/host1x/command_fifo.cpp
Normal file
147
app/src/main/cpp/skyline/soc/host1x/command_fifo.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <common/signal.h>
|
||||
#include <loader/loader.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <soc.h>
|
||||
#include "command_fifo.h"
|
||||
|
||||
namespace skyline::soc::host1x {
|
||||
/**
|
||||
* @url https://github.com/torvalds/linux/blob/477f70cd2a67904e04c2c2b9bd0fa2e95222f2f6/drivers/gpu/host1x/hw/debug_hw.c#L16
|
||||
*/
|
||||
enum class Host1xOpcode : u8 {
|
||||
SetClass = 0x00,
|
||||
Incr = 0x01,
|
||||
NonIncr = 0x02,
|
||||
Mask = 0x03,
|
||||
Imm = 0x04,
|
||||
Restart = 0x05,
|
||||
Gather = 0x06,
|
||||
SetStrmId = 0x07,
|
||||
SetAppId = 0x08,
|
||||
SetPlyd = 0x09,
|
||||
IncrW = 0x0a,
|
||||
NonIncrW = 0x0b,
|
||||
GatherW = 0x0c,
|
||||
RestartW = 0x0d,
|
||||
Extend = 0x0e,
|
||||
};
|
||||
|
||||
union ChannelCommandFifoMethodHeader {
|
||||
u32 raw{};
|
||||
u16 immdData : 12;
|
||||
u16 methodCount;
|
||||
u16 offsetMask;
|
||||
|
||||
struct {
|
||||
u8 classMethodMask : 6;
|
||||
ClassId classId : 10;
|
||||
u16 methodAddress : 12;
|
||||
Host1xOpcode opcode : 4;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ChannelCommandFifoMethodHeader) == sizeof(u32));
|
||||
|
||||
ChannelCommandFifo::ChannelCommandFifo(const DeviceState &state, SyncpointSet &syncpoints) : state(state), gatherQueue(GatherQueueSize), host1XClass(state, syncpoints), nvDecClass(state, syncpoints), vicClass(state, syncpoints) {}
|
||||
|
||||
void ChannelCommandFifo::Send(ClassId targetClass, u32 method, u32 argument) {
|
||||
state.logger->Verbose("Calling method in class: 0x{:X}, method: 0x{:X}, argument: 0x{:X}", targetClass, method, argument);
|
||||
|
||||
switch (targetClass) {
|
||||
case ClassId::Host1x:
|
||||
host1XClass.CallMethod(method, argument);
|
||||
break;
|
||||
case ClassId::NvDec:
|
||||
nvDecClass.CallMethod(method, argument);
|
||||
break;
|
||||
case ClassId::VIC:
|
||||
vicClass.CallMethod(method, argument);
|
||||
break;
|
||||
default:
|
||||
state.logger->Error("Sending method to unimplemented class: 0x{:X}", targetClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelCommandFifo::Process(span<u32> gather) {
|
||||
ClassId targetClass{ClassId::Host1x};
|
||||
|
||||
for (auto entry{gather.begin()}; entry != gather.end(); entry++) {
|
||||
ChannelCommandFifoMethodHeader methodHeader{.raw = *entry};
|
||||
|
||||
switch (methodHeader.opcode) {
|
||||
case Host1xOpcode::SetClass:
|
||||
targetClass = methodHeader.classId;
|
||||
|
||||
for (u32 i{}; i < std::numeric_limits<u8>::max(); i++)
|
||||
if (methodHeader.classMethodMask & (1 << i))
|
||||
Send(targetClass, methodHeader.methodAddress + i, *++entry);
|
||||
|
||||
break;
|
||||
case Host1xOpcode::Incr:
|
||||
for (u32 i{}; i < methodHeader.methodCount; i++)
|
||||
Send(targetClass, methodHeader.methodAddress + i, *++entry);
|
||||
|
||||
break;
|
||||
case Host1xOpcode::NonIncr:
|
||||
for (u32 i{}; i < methodHeader.methodCount; i++)
|
||||
Send(targetClass, methodHeader.methodAddress, *++entry);
|
||||
|
||||
break;
|
||||
case Host1xOpcode::Mask:
|
||||
for (u32 i{}; i < std::numeric_limits<u16>::digits; i++)
|
||||
if (methodHeader.offsetMask & (1 << i))
|
||||
Send(targetClass, methodHeader.methodAddress + i, *++entry);
|
||||
|
||||
break;
|
||||
case Host1xOpcode::Imm:
|
||||
Send(targetClass, methodHeader.methodAddress, methodHeader.immdData);
|
||||
break;
|
||||
default:
|
||||
throw exception("Unimplemented Host1x command FIFO opcode: 0x{:X}", static_cast<u8>(methodHeader.opcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelCommandFifo::Start() {
|
||||
std::scoped_lock lock(threadStartMutex);
|
||||
|
||||
if (!thread.joinable())
|
||||
thread = std::thread(&ChannelCommandFifo::Run, this);
|
||||
}
|
||||
|
||||
void ChannelCommandFifo::Run() {
|
||||
pthread_setname_np(pthread_self(), "ChannelCommandFifo");
|
||||
try {
|
||||
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler);
|
||||
|
||||
gatherQueue.Process([this](span<u32> gather) {
|
||||
state.logger->Debug("Processing pushbuffer: 0x{:X}, size: 0x{:X}", gather.data(), gather.size());
|
||||
Process(gather);
|
||||
});
|
||||
} catch (const signal::SignalException &e) {
|
||||
if (e.signal != SIGINT) {
|
||||
state.logger->Error("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
state.logger->Error(e.what());
|
||||
signal::BlockSignal({SIGINT});
|
||||
state.process->Kill(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelCommandFifo::Push(span<u32> gather) {
|
||||
gatherQueue.Push(gather);
|
||||
}
|
||||
|
||||
ChannelCommandFifo::~ChannelCommandFifo() {
|
||||
if (thread.joinable()) {
|
||||
pthread_kill(thread.native_handle(), SIGINT);
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user