Compare commits
3 Commits
master
...
mainline-0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dd828473b | ||
|
|
32eda8979f | ||
|
|
e14a756396 |
@@ -32,7 +32,8 @@ enum class CommandType : u32 {
|
||||
Control = 5,
|
||||
RequestWithContext = 6,
|
||||
ControlWithContext = 7,
|
||||
Unspecified,
|
||||
TIPC_Close = 15,
|
||||
TIPC_CommandRegion = 16, // Start of TIPC commands, this is an offset.
|
||||
};
|
||||
|
||||
struct CommandHeader {
|
||||
@@ -57,6 +58,20 @@ struct CommandHeader {
|
||||
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
|
||||
BitField<31, 1, u32> enable_handle_descriptor;
|
||||
};
|
||||
|
||||
bool IsTipc() const {
|
||||
return type.Value() >= CommandType::TIPC_CommandRegion;
|
||||
}
|
||||
|
||||
bool IsCloseCommand() const {
|
||||
switch (type.Value()) {
|
||||
case CommandType::Close:
|
||||
case CommandType::TIPC_Close:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -26,7 +28,7 @@ class RequestHelperBase {
|
||||
protected:
|
||||
Kernel::HLERequestContext* context = nullptr;
|
||||
u32* cmdbuf;
|
||||
ptrdiff_t index = 0;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
@@ -38,7 +40,7 @@ public:
|
||||
if (set_to_null) {
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
}
|
||||
index += static_cast<ptrdiff_t>(size_in_words);
|
||||
index += size_in_words;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,11 +53,11 @@ public:
|
||||
}
|
||||
|
||||
u32 GetCurrentOffset() const {
|
||||
return static_cast<u32>(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void SetCurrentOffset(u32 offset) {
|
||||
index = static_cast<ptrdiff_t>(offset);
|
||||
index = offset;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,7 +86,9 @@ public:
|
||||
|
||||
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
|
||||
// padding.
|
||||
u64 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
|
||||
u32 raw_data_size = ctx.IsTipc()
|
||||
? normal_params_size - 1
|
||||
: sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
|
||||
|
||||
u32 num_handles_to_move{};
|
||||
u32 num_domain_objects{};
|
||||
@@ -97,7 +101,11 @@ public:
|
||||
}
|
||||
|
||||
if (ctx.Session()->IsDomain()) {
|
||||
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
|
||||
raw_data_size += static_cast<u32>(sizeof(DomainMessageHeader) / 4 + num_domain_objects);
|
||||
}
|
||||
|
||||
if (ctx.IsTipc()) {
|
||||
header.type.Assign(ctx.GetCommandType());
|
||||
}
|
||||
|
||||
header.data_size.Assign(static_cast<u32>(raw_data_size));
|
||||
@@ -111,22 +119,30 @@ public:
|
||||
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy);
|
||||
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
|
||||
PushRaw(handle_descriptor_header);
|
||||
|
||||
ctx.handles_offset = index;
|
||||
|
||||
Skip(num_handles_to_copy + num_handles_to_move, true);
|
||||
}
|
||||
|
||||
AlignWithPadding();
|
||||
if (!ctx.IsTipc()) {
|
||||
AlignWithPadding();
|
||||
|
||||
if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
data_payload_index = index;
|
||||
|
||||
datapayload_index = index;
|
||||
ctx.data_payload_offset = index;
|
||||
ctx.domain_offset = index + raw_data_size / 4;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -134,6 +150,9 @@ public:
|
||||
if (context->Session()->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::Sessions, 1);
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, iface->GetServiceName());
|
||||
|
||||
@@ -152,7 +171,7 @@ public:
|
||||
const std::size_t num_move_objects = context->NumMoveObjects();
|
||||
ASSERT_MSG(!num_domain_objects || !num_move_objects,
|
||||
"cannot move normal handles and domain objects");
|
||||
ASSERT_MSG((index - datapayload_index) == normal_params_size,
|
||||
ASSERT_MSG((index - data_payload_index) == normal_params_size,
|
||||
"normal_params_size value is incorrect");
|
||||
ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move,
|
||||
"num_objects_to_move value is incorrect");
|
||||
@@ -229,14 +248,14 @@ private:
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
std::ptrdiff_t datapayload_index{};
|
||||
u32 data_payload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
|
||||
inline void ResponseBuilder::PushImpl(s32 value) {
|
||||
cmdbuf[index++] = static_cast<u32>(value);
|
||||
cmdbuf[index++] = value;
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u32 value) {
|
||||
|
||||
@@ -55,7 +55,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = rp.PopRaw<IPC::CommandHeader>();
|
||||
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
if (command_header->IsCloseCommand()) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
return;
|
||||
}
|
||||
@@ -99,39 +99,43 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
|
||||
}
|
||||
|
||||
buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
|
||||
const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
|
||||
|
||||
// Padding to align to 16 bytes
|
||||
rp.AlignWithPadding();
|
||||
if (!command_header->IsTipc()) {
|
||||
// Padding to align to 16 bytes
|
||||
rp.AlignWithPadding();
|
||||
|
||||
if (Session()->IsDomain() && ((command_header->type == IPC::CommandType::Request ||
|
||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||
!incoming)) {
|
||||
// If this is an incoming message, only CommandType "Request" has a domain header
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
||||
} else {
|
||||
if (Session()->IsDomain()) {
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
if (Session()->IsDomain() &&
|
||||
((command_header->type == IPC::CommandType::Request ||
|
||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||
!incoming)) {
|
||||
// If this is an incoming message, only CommandType "Request" has a domain header
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
||||
} else {
|
||||
if (Session()->IsDomain()) {
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
|
||||
data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
|
||||
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
|
||||
if (domain_message_header && domain_message_header->command ==
|
||||
IPC::DomainMessageHeader::CommandType::CloseVirtualHandle) {
|
||||
// CloseVirtualHandle command does not have SFC* or any data
|
||||
return;
|
||||
}
|
||||
if (domain_message_header &&
|
||||
domain_message_header->command ==
|
||||
IPC::DomainMessageHeader::CommandType::CloseVirtualHandle) {
|
||||
// CloseVirtualHandle command does not have SFC* or any data
|
||||
return;
|
||||
}
|
||||
|
||||
if (incoming) {
|
||||
ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'I'));
|
||||
} else {
|
||||
ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O'));
|
||||
if (incoming) {
|
||||
ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'I'));
|
||||
} else {
|
||||
ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O'));
|
||||
}
|
||||
}
|
||||
|
||||
rp.SetCurrentOffset(buffer_c_offset);
|
||||
@@ -166,84 +170,55 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
|
||||
u32_le* src_cmdbuf) {
|
||||
ParseCommandBuffer(handle_table, src_cmdbuf, true);
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
|
||||
if (command_header->IsCloseCommand()) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// The data_size already includes the payload header, the padding and the domain header.
|
||||
std::size_t size = data_payload_offset + command_header->data_size -
|
||||
sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
|
||||
if (domain_message_header)
|
||||
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
|
||||
std::copy_n(src_cmdbuf, size, cmd_buf.begin());
|
||||
std::copy_n(src_cmdbuf, IPC::COMMAND_BUFFER_LENGTH, cmd_buf.begin());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
|
||||
auto current_offset = handles_offset;
|
||||
auto& owner_process = *requesting_thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
memory.ReadBlock(owner_process, requesting_thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
ParseCommandBuffer(handle_table, cmd_buf.data(), false);
|
||||
|
||||
// The data_size already includes the payload header, the padding and the domain header.
|
||||
std::size_t size = data_payload_offset + command_header->data_size -
|
||||
sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
|
||||
if (domain_message_header)
|
||||
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
|
||||
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
|
||||
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
|
||||
"Handle descriptor bit set but no handles to translate");
|
||||
// We write the translated handles at a specific offset in the command buffer, this space
|
||||
// was already reserved when writing the header.
|
||||
std::size_t current_offset =
|
||||
(sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
|
||||
ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
|
||||
|
||||
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
|
||||
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
|
||||
|
||||
// We don't make a distinction between copy and move handles when translating since HLE
|
||||
// services don't deal with handles directly. However, the guest applications might check
|
||||
// for specific values in each of these descriptors.
|
||||
for (auto& object : copy_objects) {
|
||||
ASSERT(object != nullptr);
|
||||
R_TRY(handle_table.Add(&dst_cmdbuf[current_offset++], object));
|
||||
for (auto& object : copy_objects) {
|
||||
Handle handle{};
|
||||
if (object) {
|
||||
R_TRY(handle_table.Add(&handle, object));
|
||||
}
|
||||
cmd_buf[current_offset++] = handle;
|
||||
}
|
||||
for (auto& object : move_objects) {
|
||||
Handle handle{};
|
||||
if (object) {
|
||||
R_TRY(handle_table.Add(&handle, object));
|
||||
|
||||
for (auto& object : move_objects) {
|
||||
ASSERT(object != nullptr);
|
||||
R_TRY(handle_table.Add(&dst_cmdbuf[current_offset++], object));
|
||||
// Close our reference to the object, as it is being moved to the caller.
|
||||
object->Close();
|
||||
}
|
||||
cmd_buf[current_offset++] = handle;
|
||||
}
|
||||
|
||||
// TODO(Subv): Translate the X/A/B/W buffers.
|
||||
|
||||
if (Session()->IsDomain() && domain_message_header) {
|
||||
ASSERT(domain_message_header->num_objects == domain_objects.size());
|
||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||
// TODO(Subv): This completely ignores C buffers.
|
||||
std::size_t domain_offset = size - domain_message_header->num_objects;
|
||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||
// TODO(Subv): This completely ignores C buffers.
|
||||
|
||||
if (Session()->IsDomain()) {
|
||||
current_offset = domain_offset - static_cast<u32>(domain_objects.size());
|
||||
for (const auto& object : domain_objects) {
|
||||
server_session->AppendDomainRequestHandler(object);
|
||||
dst_cmdbuf[domain_offset++] =
|
||||
cmd_buf[current_offset++] =
|
||||
static_cast<u32_le>(server_session->NumDomainRequestHandlers());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(owner_process, requesting_thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
memory.WriteBlock(owner_process, requesting_thread.GetTLSAddress(), cmd_buf.data(),
|
||||
cmd_buf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
* this request (ServerSession, Originator thread, Translated command buffer, etc).
|
||||
* @returns ResultCode the result code of the translate operation.
|
||||
*/
|
||||
virtual ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) = 0;
|
||||
virtual ResultCode HandleSyncRequest(Kernel::KServerSession& session,
|
||||
Kernel::HLERequestContext& context) = 0;
|
||||
|
||||
/**
|
||||
* Signals that a client has just connected to this HLE handler and keeps the
|
||||
@@ -128,15 +129,28 @@ public:
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(KThread& requesting_thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
u32_le GetHipcCommand() const {
|
||||
return command;
|
||||
}
|
||||
|
||||
u32_le GetTipcCommand() const {
|
||||
return static_cast<u32_le>(command_header->type.Value()) -
|
||||
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
|
||||
}
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
|
||||
}
|
||||
|
||||
bool IsTipc() const {
|
||||
return command_header->IsTipc();
|
||||
}
|
||||
|
||||
IPC::CommandType GetCommandType() const {
|
||||
return command_header->type;
|
||||
}
|
||||
|
||||
unsigned GetDataPayloadOffset() const {
|
||||
u32 GetDataPayloadOffset() const {
|
||||
return data_payload_offset;
|
||||
}
|
||||
|
||||
@@ -291,8 +305,9 @@ private:
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
|
||||
std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
|
||||
|
||||
unsigned data_payload_offset{};
|
||||
unsigned buffer_c_offset{};
|
||||
u32 data_payload_offset{};
|
||||
u32 handles_offset{};
|
||||
u32 domain_offset{};
|
||||
u32_le command{};
|
||||
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
|
||||
@@ -57,7 +57,7 @@ constexpr size_t SlabCountKSharedMemory = 80;
|
||||
constexpr size_t SlabCountKTransferMemory = 200;
|
||||
constexpr size_t SlabCountKCodeMemory = 10;
|
||||
constexpr size_t SlabCountKDeviceAddressSpace = 300;
|
||||
constexpr size_t SlabCountKSession = 933;
|
||||
constexpr size_t SlabCountKSession = /*933*/ 40000;
|
||||
constexpr size_t SlabCountKLightSession = 100;
|
||||
constexpr size_t SlabCountKObjectName = 7;
|
||||
constexpr size_t SlabCountKResourceLimit = 5;
|
||||
|
||||
@@ -91,7 +91,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
|
||||
// Create a new session.
|
||||
KSession* session = KSession::Create(kernel);
|
||||
if (session == nullptr) {
|
||||
/* Decrement the session count. */
|
||||
// Decrement the session count.
|
||||
const auto prev = num_sessions--;
|
||||
if (prev == max_sessions) {
|
||||
this->NotifyAvailable();
|
||||
|
||||
@@ -95,7 +95,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
}
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(*this, context);
|
||||
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
||||
@@ -135,7 +135,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (hle_handler != nullptr) {
|
||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
||||
result = hle_handler->HandleSyncRequest(context);
|
||||
result = hle_handler->HandleSyncRequest(*this, context);
|
||||
}
|
||||
|
||||
if (convert_to_domain) {
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
|
||||
@@ -656,6 +657,7 @@ struct KernelCore::Impl {
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
|
||||
NamedPortTable named_ports;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
@@ -844,18 +846,17 @@ void KernelCore::PrepareReschedule(std::size_t id) {
|
||||
// TODO: Reimplement, this
|
||||
}
|
||||
|
||||
void KernelCore::AddNamedPort(std::string name, KClientPort* port) {
|
||||
port->Open();
|
||||
impl->named_ports.emplace(std::move(name), port);
|
||||
void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory) {
|
||||
impl->service_interface_factory.emplace(std::move(name), factory);
|
||||
}
|
||||
|
||||
KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
|
||||
return impl->named_ports.find(name);
|
||||
}
|
||||
|
||||
KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
|
||||
const std::string& name) const {
|
||||
return impl->named_ports.find(name);
|
||||
KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
|
||||
auto search = impl->service_interface_factory.find(name);
|
||||
if (search == impl->service_interface_factory.end()) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
return &search->second(impl->system.ServiceManager(), impl->system);
|
||||
}
|
||||
|
||||
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
|
||||
|
||||
@@ -27,6 +27,10 @@ class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KClientPort;
|
||||
@@ -51,6 +55,9 @@ class ServiceThread;
|
||||
class Synchronization;
|
||||
class TimeManager;
|
||||
|
||||
using ServiceInterfaceFactory =
|
||||
std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
|
||||
|
||||
namespace Init {
|
||||
struct KSlabResourceCounts;
|
||||
}
|
||||
@@ -172,14 +179,11 @@ public:
|
||||
|
||||
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, KClientPort* port);
|
||||
/// Registers a named HLE service, passing a factory used to open a port to that service.
|
||||
void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
|
||||
|
||||
/// Finds a port within the named port table with the given name.
|
||||
NamedPortTable::iterator FindNamedPort(const std::string& name);
|
||||
|
||||
/// Finds a port within the named port table with the given name.
|
||||
NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
|
||||
/// Opens a port to a service previously registered with RegisterNamedService.
|
||||
KClientPort* CreateNamedServicePort(std::string name);
|
||||
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
|
||||
@@ -284,12 +284,11 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po
|
||||
auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
// Find the client port.
|
||||
const auto it = kernel.FindNamedPort(port_name);
|
||||
if (!kernel.IsValidNamedPort(it)) {
|
||||
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
|
||||
auto port = kernel.CreateNamedServicePort(port_name);
|
||||
if (!port) {
|
||||
LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
|
||||
return ResultNotFound;
|
||||
}
|
||||
auto port = it->second;
|
||||
|
||||
// Reserve a handle for the port.
|
||||
// NOTE: Nintendo really does write directly to the output handle here.
|
||||
|
||||
@@ -68,6 +68,7 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
system.GPU().ClearCdmaInstance();
|
||||
system.GPU().MemoryManager().InvalidateQueuedCaches();
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -198,7 +198,13 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
if (vic_device) {
|
||||
// UnmapVicFrame defers texture_cache invalidation of the frame address until
|
||||
// the stream is over
|
||||
gpu.MemoryManager().UnmapVicFrame(object->dma_map_addr, *size);
|
||||
} else {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
}
|
||||
} else {
|
||||
// This occurs quite frequently, however does not seem to impact functionality
|
||||
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
|
||||
|
||||
@@ -160,6 +160,7 @@ protected:
|
||||
|
||||
s32_le nvmap_fd{};
|
||||
u32_le submit_timeout{};
|
||||
bool vic_device{};
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
namespace Service::Nvidia::Devices {
|
||||
nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
SyncpointManager& syncpoint_manager_)
|
||||
: nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {}
|
||||
|
||||
: nvhost_nvdec_common(system_, std::move(nvmap_dev_), syncpoint_manager_) {
|
||||
vic_device = true;
|
||||
}
|
||||
nvhost_vic::~nvhost_vic() = default;
|
||||
|
||||
NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
||||
@@ -111,7 +111,7 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
|
||||
port_installed = true;
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
|
||||
Kernel::KClientPort& ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
|
||||
const auto guard = LockService();
|
||||
|
||||
ASSERT(!port_installed);
|
||||
@@ -119,9 +119,10 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(max_sessions, false, service_name);
|
||||
port->GetServerPort().SetHleHandler(shared_from_this());
|
||||
kernel.AddNamedPort(service_name, &port->GetClientPort());
|
||||
|
||||
port_installed = true;
|
||||
|
||||
return port->GetClientPort();
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
|
||||
@@ -132,6 +133,16 @@ void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* function
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* functions,
|
||||
std::size_t n) {
|
||||
handlers_tipc.reserve(handlers_tipc.size() + n);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
// Usually this array is sorted by id already, so hint to insert at the end
|
||||
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header,
|
||||
functions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext& ctx,
|
||||
const FunctionInfoBase* info) {
|
||||
auto cmd_buf = ctx.CommandBuffer();
|
||||
@@ -166,33 +177,55 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
}
|
||||
|
||||
ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
|
||||
void ServiceFrameworkBase::InvokeRequestTipc(Kernel::HLERequestContext& ctx) {
|
||||
boost::container::flat_map<u32, FunctionInfoBase>::iterator itr;
|
||||
|
||||
itr = handlers_tipc.find(ctx.GetCommand());
|
||||
|
||||
const FunctionInfoBase* info = itr == handlers_tipc.end() ? nullptr : &itr->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr) {
|
||||
return ReportUnimplementedFunction(ctx, info);
|
||||
}
|
||||
|
||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
}
|
||||
|
||||
ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
const auto guard = LockService();
|
||||
|
||||
switch (context.GetCommandType()) {
|
||||
case IPC::CommandType::Close: {
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
switch (ctx.GetCommandType()) {
|
||||
case IPC::CommandType::Close:
|
||||
case IPC::CommandType::TIPC_Close: {
|
||||
session.Close();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return IPC::ERR_REMOTE_PROCESS_DEAD;
|
||||
}
|
||||
case IPC::CommandType::ControlWithContext:
|
||||
case IPC::CommandType::Control: {
|
||||
system.ServiceManager().InvokeControlRequest(context);
|
||||
system.ServiceManager().InvokeControlRequest(ctx);
|
||||
break;
|
||||
}
|
||||
case IPC::CommandType::RequestWithContext:
|
||||
case IPC::CommandType::Request: {
|
||||
InvokeRequest(context);
|
||||
InvokeRequest(ctx);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());
|
||||
if (ctx.IsTipc()) {
|
||||
InvokeRequestTipc(ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
|
||||
}
|
||||
|
||||
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||
// memory that may be shutting down as well.
|
||||
if (system.IsPoweredOn()) {
|
||||
context.WriteToOutgoingCommandBuffer(context.GetThread());
|
||||
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
@@ -207,7 +240,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm, system);
|
||||
system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
|
||||
|
||||
Account::InstallInterfaces(system);
|
||||
AM::InstallInterfaces(*sm, *nv_flinger, system);
|
||||
|
||||
@@ -21,7 +21,9 @@ class System;
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
class KClientPort;
|
||||
class KServerSession;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service {
|
||||
|
||||
@@ -64,12 +66,19 @@ public:
|
||||
|
||||
/// Creates a port pair and registers this service with the given ServiceManager.
|
||||
void InstallAsService(SM::ServiceManager& service_manager);
|
||||
/// Creates a port pair and registers it on the kernel's global port registry.
|
||||
void InstallAsNamedPort(Kernel::KernelCore& kernel);
|
||||
/// Invokes a service request routine.
|
||||
|
||||
/// Invokes a service request routine using the HIPC protocol.
|
||||
void InvokeRequest(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/// Invokes a service request routine using the HIPC protocol.
|
||||
void InvokeRequestTipc(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/// Creates a port pair and registers it on the kernel's global port registry.
|
||||
Kernel::KClientPort& CreatePort(Kernel::KernelCore& kernel);
|
||||
|
||||
/// Handles a synchronization request for the service.
|
||||
ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;
|
||||
ResultCode HandleSyncRequest(Kernel::KServerSession& session,
|
||||
Kernel::HLERequestContext& context) override;
|
||||
|
||||
protected:
|
||||
/// Member-function pointer type of SyncRequest handlers.
|
||||
@@ -102,6 +111,7 @@ private:
|
||||
~ServiceFrameworkBase() override;
|
||||
|
||||
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
|
||||
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
|
||||
void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
|
||||
|
||||
/// Identifier string used to connect to the service.
|
||||
@@ -116,6 +126,7 @@ private:
|
||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||
InvokerFn* handler_invoker;
|
||||
boost::container::flat_map<u32, FunctionInfoBase> handlers;
|
||||
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
|
||||
|
||||
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||
Common::SpinLock lock_service;
|
||||
@@ -183,6 +194,20 @@ protected:
|
||||
RegisterHandlersBase(functions, n);
|
||||
}
|
||||
|
||||
/// Registers handlers in the service.
|
||||
template <std::size_t N>
|
||||
void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) {
|
||||
RegisterHandlersTipc(functions, N);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
|
||||
* overload in order to avoid needing to specify the array size.
|
||||
*/
|
||||
void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) {
|
||||
RegisterHandlersBaseTipc(functions, n);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* This function is used to allow invocation of pointers to handlers stored in the base class
|
||||
|
||||
@@ -22,21 +22,29 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(1); // Converted sessions start with 1 request handler
|
||||
}
|
||||
|
||||
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
|
||||
// and that we probably want to actually make an entirely new Session, but we still need to
|
||||
// verify this on hardware.
|
||||
|
||||
LOG_DEBUG(Service, "called");
|
||||
|
||||
auto session = ctx.Session()->GetParent();
|
||||
|
||||
// Open a reference to the session to simulate a new one being created.
|
||||
session->Open();
|
||||
session->GetClientSession().Open();
|
||||
session->GetServerSession().Open();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushMoveObjects(ctx.Session()->GetParent()->GetClientSession());
|
||||
rb.PushMoveObjects(session->GetClientSession());
|
||||
}
|
||||
|
||||
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
|
||||
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service, "called");
|
||||
|
||||
CloneCurrentObject(ctx);
|
||||
DuplicateSession(ctx);
|
||||
}
|
||||
|
||||
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
@@ -44,7 +52,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u16>(0x1000);
|
||||
rb.Push<u16>(0x8000);
|
||||
}
|
||||
|
||||
// https://switchbrew.org/wiki/IPC_Marshalling
|
||||
@@ -52,9 +60,9 @@ Controller::Controller(Core::System& system_) : ServiceFramework{system_, "IpcCo
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
|
||||
{1, nullptr, "CopyFromCurrentDomain"},
|
||||
{2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
|
||||
{2, &Controller::DuplicateSession, "DuplicateSession"},
|
||||
{3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
|
||||
{4, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ public:
|
||||
|
||||
private:
|
||||
void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObject(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSession(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
|
||||
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/kernel/k_port.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
|
||||
namespace Service::SM {
|
||||
|
||||
constexpr ResultCode ERR_NOT_INITIALIZED(ErrorModule::SM, 2);
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
|
||||
constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
|
||||
constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
|
||||
@@ -34,20 +36,17 @@ static ResultCode ValidateServiceName(const std::string& name) {
|
||||
LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
|
||||
return ERR_INVALID_NAME;
|
||||
}
|
||||
if (name.rfind('\0') != std::string::npos) {
|
||||
LOG_ERROR(Service_SM, "A non null terminated service was passed");
|
||||
return ERR_INVALID_NAME;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self, Core::System& system) {
|
||||
ASSERT(self->sm_interface.expired());
|
||||
Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) {
|
||||
ASSERT(self.sm_interface.expired());
|
||||
|
||||
auto sm = std::make_shared<SM>(self, system);
|
||||
sm->InstallAsNamedPort(system.Kernel());
|
||||
self->sm_interface = sm;
|
||||
self->controller_interface = std::make_unique<Controller>(system);
|
||||
self.sm_interface = sm;
|
||||
self.controller_interface = std::make_unique<Controller>(system);
|
||||
|
||||
return sm->CreatePort(system.Kernel());
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name,
|
||||
@@ -107,33 +106,68 @@ SM::~SM() = default;
|
||||
void SM::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SM, "called");
|
||||
|
||||
is_initialized = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
|
||||
auto result = service_manager->GetServicePort(name);
|
||||
if (result.Failed()) {
|
||||
auto result = GetServiceImpl(ctx);
|
||||
if (result.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result.Code());
|
||||
rb.PushMoveObjects(result.Unwrap());
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
}
|
||||
}
|
||||
|
||||
void SM::GetServiceTipc(Kernel::HLERequestContext& ctx) {
|
||||
auto result = GetServiceImpl(ctx);
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result.Code());
|
||||
rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr);
|
||||
}
|
||||
|
||||
static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
std::string result;
|
||||
for (const auto& c : name_buf) {
|
||||
if (c >= ' ' && c <= '~') {
|
||||
result.push_back(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& ctx) {
|
||||
if (!is_initialized) {
|
||||
return ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
std::string name(PopServiceName(rp));
|
||||
|
||||
auto result = service_manager.GetServicePort(name);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.Code().raw);
|
||||
if (name.length() == 0)
|
||||
return; // LibNX Fix
|
||||
UNIMPLEMENTED();
|
||||
return;
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
auto* port = result.Unwrap();
|
||||
|
||||
Kernel::KScopedResourceReservation session_reservation(
|
||||
kernel.CurrentProcess()->GetResourceLimit(), Kernel::LimitableResource::Sessions);
|
||||
R_UNLESS(session_reservation.Succeeded(), Kernel::ResultLimitReached);
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(&port->GetClientPort(), std::move(name));
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
if (port->GetServerPort().GetHLEHandler()) {
|
||||
port->GetServerPort().GetHLEHandler()->ClientConnected(&session->GetServerSession());
|
||||
} else {
|
||||
@@ -141,18 +175,12 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushMoveObjects(session->GetClientSession());
|
||||
return MakeResult(&session->GetClientSession());
|
||||
}
|
||||
|
||||
void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
const std::string name(name_buf.begin(), end);
|
||||
std::string name(PopServiceName(rp));
|
||||
|
||||
const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
|
||||
const auto max_session_count = rp.PopRaw<u32>();
|
||||
@@ -160,7 +188,7 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
||||
max_session_count, is_light);
|
||||
|
||||
auto handle = service_manager->RegisterService(name, max_session_count);
|
||||
auto handle = service_manager.RegisterService(name, max_session_count);
|
||||
if (handle.Failed()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
||||
handle.Code().raw);
|
||||
@@ -178,28 +206,31 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
std::string name(PopServiceName(rp));
|
||||
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
const std::string name(name_buf.begin(), end);
|
||||
LOG_DEBUG(Service_SM, "called with name={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(service_manager->UnregisterService(name));
|
||||
rb.Push(service_manager.UnregisterService(name));
|
||||
}
|
||||
|
||||
SM::SM(std::shared_ptr<ServiceManager> service_manager_, Core::System& system_)
|
||||
SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
||||
: ServiceFramework{system_, "sm:", 4},
|
||||
service_manager{std::move(service_manager_)}, kernel{system_.Kernel()} {
|
||||
static const FunctionInfo functions[] = {
|
||||
service_manager{service_manager_}, kernel{system_.Kernel()} {
|
||||
RegisterHandlers({
|
||||
{0, &SM::Initialize, "Initialize"},
|
||||
{1, &SM::GetService, "GetService"},
|
||||
{2, &SM::RegisterService, "RegisterService"},
|
||||
{3, &SM::UnregisterService, "UnregisterService"},
|
||||
{4, nullptr, "DetachClient"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
});
|
||||
RegisterHandlersTipc({
|
||||
{0, &SM::Initialize, "Initialize"},
|
||||
{1, &SM::GetServiceTipc, "GetService"},
|
||||
{2, &SM::RegisterService, "RegisterService"},
|
||||
{3, &SM::UnregisterService, "UnregisterService"},
|
||||
{4, nullptr, "DetachClient"},
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Service::SM
|
||||
|
||||
@@ -34,22 +34,26 @@ class Controller;
|
||||
/// Interface to "sm:" service
|
||||
class SM final : public ServiceFramework<SM> {
|
||||
public:
|
||||
explicit SM(std::shared_ptr<ServiceManager> service_manager_, Core::System& system_);
|
||||
explicit SM(ServiceManager& service_manager_, Core::System& system_);
|
||||
~SM() override;
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void GetService(Kernel::HLERequestContext& ctx);
|
||||
void GetServiceTipc(Kernel::HLERequestContext& ctx);
|
||||
void RegisterService(Kernel::HLERequestContext& ctx);
|
||||
void UnregisterService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<ServiceManager> service_manager;
|
||||
ResultVal<Kernel::KClientSession*> GetServiceImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
ServiceManager& service_manager;
|
||||
bool is_initialized{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
class ServiceManager {
|
||||
public:
|
||||
static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Core::System& system);
|
||||
static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
|
||||
|
||||
explicit ServiceManager(Kernel::KernelCore& kernel_);
|
||||
~ServiceManager();
|
||||
|
||||
@@ -1785,6 +1785,8 @@ public:
|
||||
SSY,
|
||||
SYNC,
|
||||
BRK,
|
||||
CAL,
|
||||
RET,
|
||||
DEPBAR,
|
||||
VOTE,
|
||||
VOTE_VTG,
|
||||
@@ -2108,6 +2110,8 @@ private:
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("111000100110----", Id::CAL, Type::Flow, "CAL"),
|
||||
INST("111000110010----", Id::RET, Type::Flow, "RET"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||
INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"),
|
||||
|
||||
@@ -114,6 +114,25 @@ void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
void MemoryManager::UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size) {
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
rasterizer->InvalidateExceptTextureCache(*cpu_addr, size);
|
||||
cache_invalidate_queue.push_back({*cpu_addr, size});
|
||||
|
||||
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
|
||||
}
|
||||
|
||||
void MemoryManager::InvalidateQueuedCaches() {
|
||||
for (const auto& entry : cache_invalidate_queue) {
|
||||
rasterizer->InvalidateTextureCache(entry.first, entry.second);
|
||||
}
|
||||
cache_invalidate_queue.clear();
|
||||
}
|
||||
PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
|
||||
return page_table[PageEntryIndex(gpu_addr)];
|
||||
}
|
||||
|
||||
@@ -123,6 +123,14 @@ public:
|
||||
[[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
|
||||
void Unmap(GPUVAddr gpu_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Some Decoded NVDEC frames require that texture cache does not get invalidated.
|
||||
* UnmapVicFrame defers the texture cache invalidation until the stream ends
|
||||
* by invoking InvalidateQueuedCaches to invalidate all frame texture caches.
|
||||
*/
|
||||
void UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size);
|
||||
void InvalidateQueuedCaches();
|
||||
|
||||
private:
|
||||
[[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
|
||||
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
|
||||
|
||||
@@ -69,6 +69,12 @@ public:
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
virtual void FlushRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer to flush the texture cache to Switch memory
|
||||
virtual void InvalidateExceptTextureCache(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer to invalidate the texture cache
|
||||
virtual void InvalidateTextureCache(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Check if the the specified memory area requires flushing to CPU Memory.
|
||||
virtual bool MustFlushRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
|
||||
@@ -491,6 +491,9 @@ private:
|
||||
const Registry& registry;
|
||||
const ShaderType stage;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
u32 ast_var_base{};
|
||||
|
||||
std::size_t num_temporaries = 0;
|
||||
std::size_t max_temporaries = 0;
|
||||
|
||||
@@ -807,13 +810,33 @@ ARBDecompiler::ARBDecompiler(const Device& device_, const ShaderIR& ir_, const R
|
||||
: device{device_}, ir{ir_}, registry{registry_}, stage{stage_} {
|
||||
DefineGlobalMemory();
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
ast_var_base = 0;
|
||||
|
||||
AddLine("TEMP RC;");
|
||||
AddLine("TEMP FSWZA[4];");
|
||||
AddLine("TEMP FSWZB[4];");
|
||||
if (ir.IsDecompiled()) {
|
||||
InitializeVariables();
|
||||
AddLine("main:");
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
AddLine("RET;");
|
||||
}
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
auto it = subfunctions.begin();
|
||||
while (it != subfunctions.end()) {
|
||||
context_func = *it;
|
||||
AddLine("func_{}:", context_func->GetId());
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
AddLine("RET;");
|
||||
}
|
||||
it++;
|
||||
}
|
||||
AddLine("END");
|
||||
|
||||
@@ -1060,41 +1083,38 @@ void ARBDecompiler::InitializeVariables() {
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("TEMP F{};", i);
|
||||
AddLine("TEMP F{};", i + ast_var_base);
|
||||
}
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i);
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i + ast_var_base);
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
VisitAST(ir.GetASTProgram());
|
||||
VisitAST(context_func->GetASTProgram());
|
||||
ast_var_base += num_flow_variables;
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileBranchMode() {
|
||||
static constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP SSY_TOP;");
|
||||
AddLine("TEMP PBK_TOP;");
|
||||
}
|
||||
|
||||
AddLine("TEMP PC;");
|
||||
AddLine("TEMP PC{};", context_func->GetId());
|
||||
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("MOV.U SSY_TOP.x, 0;");
|
||||
AddLine("MOV.U PBK_TOP.x, 0;");
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
const auto basic_block_end = ir.GetBasicBlocks().end();
|
||||
auto basic_block_it = ir.GetBasicBlocks().begin();
|
||||
const auto basic_block_end = context_func->GetBasicBlocks().end();
|
||||
auto basic_block_it = context_func->GetBasicBlocks().begin();
|
||||
const u32 first_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", first_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), first_address);
|
||||
|
||||
AddLine("REP;");
|
||||
|
||||
@@ -1103,7 +1123,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto& [address, bb] = *basic_block_it;
|
||||
++num_blocks;
|
||||
|
||||
AddLine("SEQ.S.CC RC.x, PC.x, {};", address);
|
||||
AddLine("SEQ.S.CC RC.x, PC{}.x, {};", context_func->GetId(), address);
|
||||
AddLine("IF NE.x;");
|
||||
|
||||
VisitBlock(bb);
|
||||
@@ -1114,7 +1134,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
|
||||
if (!op || op->GetCode() != OperationCode::Branch) {
|
||||
const u32 next_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", next_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), next_address);
|
||||
AddLine("CONT;");
|
||||
}
|
||||
}
|
||||
@@ -1152,7 +1172,8 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
} else if (const auto decoded = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
|
||||
VisitBlock(decoded->nodes);
|
||||
} else if (const auto var_set = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
|
||||
AddLine("MOV.U F{}, {};", var_set->index, VisitExpression(var_set->condition));
|
||||
AddLine("MOV.U F{}, {};", var_set->index + ast_var_base,
|
||||
VisitExpression(var_set->condition));
|
||||
ResetTemporaries();
|
||||
} else if (const auto do_while = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
|
||||
const std::string condition = VisitExpression(do_while->condition);
|
||||
@@ -1172,7 +1193,11 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
ResetTemporaries();
|
||||
}
|
||||
if (ast_return->kills) {
|
||||
AddLine("KIL TR;");
|
||||
if (stage == ShaderType::Fragment) {
|
||||
AddLine("KIL TR;");
|
||||
} else {
|
||||
AddLine("RET;");
|
||||
}
|
||||
} else {
|
||||
Exit();
|
||||
}
|
||||
@@ -1219,7 +1244,7 @@ std::string ARBDecompiler::VisitExpression(const Expr& node) {
|
||||
return Visit(ir.GetConditionCode(expr->cc));
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprVar>(&*node)) {
|
||||
return fmt::format("F{}.x", expr->var_index);
|
||||
return fmt::format("F{}.x", expr->var_index + ast_var_base);
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
|
||||
return expr->value ? "0xffffffff" : "0";
|
||||
@@ -1406,6 +1431,11 @@ std::string ARBDecompiler::Visit(const Node& node) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
AddLine("CAL func_{};", func_call->GetFuncId());
|
||||
return {};
|
||||
}
|
||||
|
||||
if ([[maybe_unused]] const auto cmt = std::get_if<CommentNode>(&*node)) {
|
||||
// Uncommenting this will generate invalid code. GLASM lacks comments.
|
||||
// AddLine("// {}", cmt->GetText());
|
||||
@@ -1479,7 +1509,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) {
|
||||
}
|
||||
|
||||
void ARBDecompiler::Exit() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
if (!context_func->IsMain() || stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return;
|
||||
}
|
||||
@@ -2021,13 +2051,13 @@ std::string ARBDecompiler::ImageStore(Operation operation) {
|
||||
|
||||
std::string ARBDecompiler::Branch(Operation operation) {
|
||||
const auto target = std::get<ImmediateNode>(*operation[0]);
|
||||
AddLine("MOV.U PC.x, {};", target.GetValue());
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), target.GetValue());
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::BranchIndirect(Operation operation) {
|
||||
AddLine("MOV.U PC.x, {};", Visit(operation[0]));
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), Visit(operation[0]));
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2045,7 +2075,7 @@ std::string ARBDecompiler::PopFlowStack(Operation operation) {
|
||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||
const std::string_view stack_name = StackName(stack);
|
||||
AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC{}.x, {}[{}_TOP.x].x;", context_func->GetId(), stack_name, stack_name);
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2056,6 +2086,10 @@ std::string ARBDecompiler::Exit(Operation) {
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::Discard(Operation) {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return {};
|
||||
}
|
||||
AddLine("KIL TR;");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -544,6 +544,26 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateExceptTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
shader_cache.InvalidateRegion(addr, size);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapMemory(addr, size);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
if (!Settings::IsGPULevelHigh()) {
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
|
||||
void InvalidateTextureCache(VAddr addr, u64 size) override;
|
||||
bool MustFlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
|
||||
@@ -435,6 +435,27 @@ public:
|
||||
DeclareCustomVariables();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
auto it = subfunctions.rbegin();
|
||||
while (it != subfunctions.rend()) {
|
||||
context_func = *it;
|
||||
code.AddLine("void func_{}() {{", context_func->GetId());
|
||||
++code.scope;
|
||||
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
}
|
||||
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
|
||||
code.AddLine("void main() {{");
|
||||
++code.scope;
|
||||
|
||||
@@ -442,7 +463,7 @@ public:
|
||||
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
|
||||
}
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
@@ -462,13 +483,13 @@ private:
|
||||
|
||||
void DecompileBranchMode() {
|
||||
// VM's program counter
|
||||
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const auto first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
code.AddLine("uint jmp_to = {}U;", first_address);
|
||||
|
||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
||||
@@ -480,7 +501,7 @@ private:
|
||||
|
||||
code.AddLine("switch (jmp_to) {{");
|
||||
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const auto& [address, bb] = pair;
|
||||
code.AddLine("case 0x{:X}U: {{", address);
|
||||
++code.scope;
|
||||
@@ -1131,6 +1152,11 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
code.AddLine("func_{}();", func_call->GetFuncId());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
code.AddLine("// " + comment->GetText());
|
||||
return {};
|
||||
@@ -2267,7 +2293,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
code.AddLine("return;");
|
||||
return {};
|
||||
}
|
||||
@@ -2277,7 +2305,11 @@ private:
|
||||
// about unexecuted instructions that may follow this.
|
||||
code.AddLine("if (true) {{");
|
||||
++code.scope;
|
||||
code.AddLine("discard;");
|
||||
if (stage != ShaderType::Fragment) {
|
||||
code.AddLine("return;");
|
||||
} else {
|
||||
code.AddLine("discard;");
|
||||
}
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
return {};
|
||||
@@ -2388,7 +2420,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2755,6 +2787,8 @@ private:
|
||||
const Header header;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
ShaderWriter code;
|
||||
|
||||
std::optional<u32> max_input_vertices;
|
||||
@@ -2902,9 +2936,15 @@ public:
|
||||
decomp.code.scope++;
|
||||
}
|
||||
if (ast.kills) {
|
||||
decomp.code.AddLine("discard;");
|
||||
if (decomp.stage != ShaderType::Fragment) {
|
||||
decomp.code.AddLine("return;");
|
||||
} else {
|
||||
decomp.code.AddLine("discard;");
|
||||
}
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.code.AddLine("return;");
|
||||
}
|
||||
if (!is_true) {
|
||||
@@ -2937,13 +2977,13 @@ private:
|
||||
};
|
||||
|
||||
void GLSLDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
||||
}
|
||||
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(ir.GetASTProgram());
|
||||
decompiler.Visit(context_func->GetASTProgram());
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -493,6 +493,26 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void Vulkan::RasterizerVulkan::InvalidateExceptTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
pipeline_cache.InvalidateRegion(addr, size);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void Vulkan::RasterizerVulkan::InvalidateTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapMemory(addr, size);
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
|
||||
std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
|
||||
if (!Settings::IsGPULevelHigh()) {
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
|
||||
void InvalidateTextureCache(VAddr addr, u64 size) override;
|
||||
bool MustFlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
|
||||
@@ -406,10 +406,38 @@ private:
|
||||
binding = DeclareStorageTexels(binding);
|
||||
binding = DeclareImages(binding);
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
|
||||
labels.resize(subfunctions.size() + 1);
|
||||
other_functions.resize(subfunctions.size());
|
||||
|
||||
auto it = subfunctions.rbegin();
|
||||
while (it != subfunctions.rend()) {
|
||||
context_func = *it;
|
||||
other_functions[context_func->GetId() - 1] =
|
||||
OpFunction(t_void, {}, TypeFunction(t_void));
|
||||
AddLabel();
|
||||
|
||||
if (context_func->IsDecompiled()) {
|
||||
DeclareFlowVariables();
|
||||
DecompileAST();
|
||||
} else {
|
||||
AllocateLabels();
|
||||
DecompileBranchMode();
|
||||
}
|
||||
|
||||
OpReturn();
|
||||
OpFunctionEnd();
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
|
||||
const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
|
||||
AddLabel();
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DeclareFlowVariables();
|
||||
DecompileAST();
|
||||
} else {
|
||||
@@ -441,16 +469,18 @@ private:
|
||||
void DecompileAST();
|
||||
|
||||
void DecompileBranchMode() {
|
||||
const u32 first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const Id loop_label = OpLabel("loop");
|
||||
const Id merge_label = OpLabel("merge");
|
||||
const u32 first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
const u32 func_id = context_func->GetId();
|
||||
const std::string func_id_msg = std::to_string(func_id);
|
||||
const Id loop_label = OpLabel("loop_" + func_id_msg);
|
||||
const Id merge_label = OpLabel("merge_" + func_id_msg);
|
||||
const Id dummy_label = OpLabel();
|
||||
const Id jump_label = OpLabel();
|
||||
continue_label = OpLabel("continue");
|
||||
continue_label = OpLabel("continue_" + func_id_msg);
|
||||
|
||||
std::vector<Sirit::Literal> literals;
|
||||
std::vector<Id> branch_labels;
|
||||
for (const auto& [literal, label] : labels) {
|
||||
for (const auto& [literal, label] : labels[func_id]) {
|
||||
literals.push_back(literal);
|
||||
branch_labels.push_back(label);
|
||||
}
|
||||
@@ -462,11 +492,11 @@ private:
|
||||
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
||||
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
||||
|
||||
Name(jmp_to, "jmp_to");
|
||||
Name(ssy_flow_stack, "ssy_flow_stack");
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
||||
Name(pbk_flow_stack, "pbk_flow_stack");
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
||||
Name(jmp_to, "jmp_to_" + func_id_msg);
|
||||
Name(ssy_flow_stack, "ssy_flow_stack_" + func_id_msg);
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top_" + func_id_msg);
|
||||
Name(pbk_flow_stack, "pbk_flow_stack_" + func_id_msg);
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top_" + func_id_msg);
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
@@ -484,13 +514,14 @@ private:
|
||||
AddLabel(default_branch);
|
||||
OpReturn();
|
||||
|
||||
for (const auto& [address, bb] : ir.GetBasicBlocks()) {
|
||||
AddLabel(labels.at(address));
|
||||
for (const auto& [address, bb] : context_func->GetBasicBlocks()) {
|
||||
AddLabel(labels[func_id].at(address));
|
||||
|
||||
VisitBasicBlock(bb);
|
||||
|
||||
const auto next_it = labels.lower_bound(address + 1);
|
||||
const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
|
||||
const auto next_it = labels[func_id].lower_bound(address + 1);
|
||||
const Id next_label =
|
||||
next_it != labels[func_id].end() ? next_it->second : default_branch;
|
||||
OpBranch(next_label);
|
||||
}
|
||||
|
||||
@@ -508,9 +539,10 @@ private:
|
||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||
|
||||
void AllocateLabels() {
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
const u32 func_id = context_func->GetId();
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const u32 address = pair.first;
|
||||
labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
labels[func_id].emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +621,14 @@ private:
|
||||
DeclareOutputVertex();
|
||||
}
|
||||
|
||||
void SafeKill() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
OpReturn();
|
||||
return;
|
||||
}
|
||||
OpKill();
|
||||
}
|
||||
|
||||
void DeclareFragment() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
return;
|
||||
@@ -656,7 +696,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareFlowVariables() {
|
||||
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
|
||||
for (u32 i = 0; i < context_func->GetASTNumVariables(); i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
||||
flow_variables.emplace(i, AddGlobalVariable(id));
|
||||
@@ -1333,6 +1373,12 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
const u32 func_id = func_call->GetFuncId();
|
||||
OpFunctionCall(t_void, other_functions[func_id - 1]);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
// We should insert comments with OpString instead of using named variables
|
||||
@@ -2124,7 +2170,7 @@ private:
|
||||
|
||||
OpBranchConditional(condition, true_label, discard_label);
|
||||
AddLabel(discard_label);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel(true_label);
|
||||
}
|
||||
|
||||
@@ -2175,7 +2221,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpReturn();
|
||||
@@ -2192,12 +2240,12 @@ private:
|
||||
Expression Discard(Operation operation) {
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpKill();
|
||||
SafeKill();
|
||||
} else {
|
||||
const Id dummy = OpLabel();
|
||||
OpBranch(dummy);
|
||||
AddLabel(dummy);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel();
|
||||
}
|
||||
return {};
|
||||
@@ -2276,7 +2324,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2770,6 +2818,8 @@ private:
|
||||
const Specialization& specialization;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
const Id t_void = Name(TypeVoid(), "void");
|
||||
|
||||
const Id t_bool = Name(TypeBool(), "bool");
|
||||
@@ -2896,7 +2946,8 @@ private:
|
||||
Id ssy_flow_stack{};
|
||||
Id pbk_flow_stack{};
|
||||
Id continue_label{};
|
||||
std::map<u32, Id> labels;
|
||||
std::vector<std::map<u32, Id>> labels;
|
||||
std::vector<Id> other_functions;
|
||||
|
||||
bool conditional_branch_set{};
|
||||
bool inside_branch{};
|
||||
@@ -3047,9 +3098,11 @@ public:
|
||||
decomp.OpBranchConditional(condition, then_label, endif_label);
|
||||
decomp.AddLabel(then_label);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(endif_label);
|
||||
@@ -3058,9 +3111,11 @@ public:
|
||||
decomp.OpBranch(next_block);
|
||||
decomp.AddLabel(next_block);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(decomp.OpLabel());
|
||||
@@ -3097,7 +3152,7 @@ private:
|
||||
};
|
||||
|
||||
void SPIRVDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", i));
|
||||
@@ -3106,7 +3161,7 @@ void SPIRVDecompiler::DecompileAST() {
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
const ASTNode program = ir.GetASTProgram();
|
||||
const ASTNode program = context_func->GetASTProgram();
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(program);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -26,17 +27,29 @@ using Tegra::Shader::OpCode;
|
||||
|
||||
constexpr s32 unassigned_branch = -2;
|
||||
|
||||
enum class JumpLabel : u32 {
|
||||
SSYClass = 0,
|
||||
PBKClass = 1,
|
||||
};
|
||||
|
||||
struct JumpItem {
|
||||
JumpLabel type;
|
||||
u32 address;
|
||||
|
||||
bool operator==(const JumpItem& other) const {
|
||||
return std::tie(type, address) == std::tie(other.type, other.address);
|
||||
}
|
||||
};
|
||||
|
||||
struct Query {
|
||||
u32 address{};
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
struct BlockStack {
|
||||
BlockStack() = default;
|
||||
explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
explicit BlockStack(const Query& q) : stack{q.stack} {}
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
@@ -65,20 +78,36 @@ struct BlockInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct CFGRebuildState {
|
||||
explicit CFGRebuildState(const ProgramCode& program_code_, u32 start_, Registry& registry_)
|
||||
: program_code{program_code_}, registry{registry_}, start{start_} {}
|
||||
struct ProgramControl {
|
||||
std::unordered_set<u32> found_functions{};
|
||||
std::list<u32> pending_functions{};
|
||||
|
||||
void RegisterFunction(u32 address) {
|
||||
if (found_functions.count(address) != 0) {
|
||||
return;
|
||||
}
|
||||
found_functions.insert(address);
|
||||
pending_functions.emplace_back(address);
|
||||
}
|
||||
};
|
||||
|
||||
struct CFGRebuildState {
|
||||
explicit CFGRebuildState(ProgramControl& control_, const ProgramCode& program_code_, u32 start_,
|
||||
u32 base_start_, Registry& registry_)
|
||||
: control{control_}, program_code{program_code_}, registry{registry_}, start{start_},
|
||||
base_start{base_start_} {}
|
||||
|
||||
ProgramControl& control;
|
||||
const ProgramCode& program_code;
|
||||
Registry& registry;
|
||||
u32 start{};
|
||||
u32 base_start{};
|
||||
std::vector<BlockInfo> block_info;
|
||||
std::list<u32> inspect_queries;
|
||||
std::list<Query> queries;
|
||||
std::unordered_map<u32, u32> registered;
|
||||
std::set<u32> labels;
|
||||
std::map<u32, u32> ssy_labels;
|
||||
std::map<u32, u32> pbk_labels;
|
||||
std::map<u32, JumpItem> jump_labels;
|
||||
std::unordered_map<u32, BlockStack> stacks;
|
||||
ASTManager* manager{};
|
||||
};
|
||||
@@ -153,7 +182,7 @@ template <typename Result, typename TestCallable, typename PackCallable>
|
||||
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
||||
PackCallable pack) {
|
||||
for (; pos >= state.start; --pos) {
|
||||
if (IsSchedInstruction(pos, state.start)) {
|
||||
if (IsSchedInstruction(pos, state.base_start)) {
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = state.program_code[pos];
|
||||
@@ -262,7 +291,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
single_branch.ignore = true;
|
||||
break;
|
||||
}
|
||||
if (IsSchedInstruction(offset, state.start)) {
|
||||
if (IsSchedInstruction(offset, state.base_start)) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
@@ -274,6 +303,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
}
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const auto pred_index = static_cast<u32>(instr.pred.pred_index);
|
||||
single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0);
|
||||
@@ -411,13 +441,20 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
case OpCode::Id::SSY: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.ssy_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::SSYClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::PBK: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.pbk_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::PBKClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::CAL: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
state.control.RegisterFunction(target);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRX: {
|
||||
@@ -513,7 +550,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||
}
|
||||
|
||||
bool TryQuery(CFGRebuildState& state) {
|
||||
const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels,
|
||||
const auto gather_labels = [](std::stack<JumpItem>& cc, std::map<u32, JumpItem>& labels,
|
||||
BlockInfo& block) {
|
||||
auto gather_start = labels.lower_bound(block.start);
|
||||
const auto gather_end = labels.upper_bound(block.end);
|
||||
@@ -522,6 +559,19 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
++gather_start;
|
||||
}
|
||||
};
|
||||
const auto pop_labels = [](JumpLabel type, SingleBranch* branch, Query& query) -> bool {
|
||||
while (!query.stack.empty() && query.stack.top().type != type) {
|
||||
query.stack.pop();
|
||||
}
|
||||
if (query.stack.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = query.stack.top().address;
|
||||
}
|
||||
query.stack.pop();
|
||||
return true;
|
||||
};
|
||||
if (state.queries.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -534,8 +584,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
// consumes a label. Schedule new queries accordingly
|
||||
if (block.visited) {
|
||||
BlockStack& stack = state.stacks[q.address];
|
||||
const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) &&
|
||||
(stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack);
|
||||
const bool all_okay = (stack.stack.empty() || q.stack == stack.stack);
|
||||
state.queries.pop_front();
|
||||
return all_okay;
|
||||
}
|
||||
@@ -544,8 +593,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
Query q2(q);
|
||||
state.queries.pop_front();
|
||||
gather_labels(q2.ssy_stack, state.ssy_labels, block);
|
||||
gather_labels(q2.pbk_stack, state.pbk_labels, block);
|
||||
gather_labels(q2.stack, state.jump_labels, block);
|
||||
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
||||
auto* branch = std::get_if<SingleBranch>(block.branch.get());
|
||||
if (!branch->condition.IsUnconditional()) {
|
||||
@@ -555,16 +603,10 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
auto& conditional_query = state.queries.emplace_back(q2);
|
||||
if (branch->is_sync) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.ssy_stack.top();
|
||||
}
|
||||
conditional_query.ssy_stack.pop();
|
||||
pop_labels(JumpLabel::SSYClass, branch, conditional_query);
|
||||
}
|
||||
if (branch->is_brk) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.pbk_stack.top();
|
||||
}
|
||||
conditional_query.pbk_stack.pop();
|
||||
pop_labels(JumpLabel::PBKClass, branch, conditional_query);
|
||||
}
|
||||
conditional_query.address = branch->address;
|
||||
return true;
|
||||
@@ -646,25 +688,23 @@ void DecompileShader(CFGRebuildState& state) {
|
||||
state.manager->Decompile();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||
ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_code,
|
||||
u32 start_address, u32 base_start, const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
ShaderFunction result_out{};
|
||||
if (settings.depth == CompileDepth::BruteForce) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
|
||||
CFGRebuildState state{program_code, start_address, registry};
|
||||
CFGRebuildState state{control, program_code, start_address, base_start, registry};
|
||||
// Inspect Code and generate blocks
|
||||
state.labels.clear();
|
||||
state.labels.emplace(start_address);
|
||||
state.inspect_queries.push_back(state.start);
|
||||
while (!state.inspect_queries.empty()) {
|
||||
if (!TryInspectAddress(state)) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
@@ -675,7 +715,7 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
if (settings.depth != CompileDepth::FlowStack) {
|
||||
// Decompile Stacks
|
||||
state.queries.push_back(Query{state.start, {}, {}});
|
||||
state.queries.push_back(Query{state.start, {}});
|
||||
decompiled = true;
|
||||
while (!state.queries.empty()) {
|
||||
if (!TryQuery(state)) {
|
||||
@@ -705,19 +745,18 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
state.manager->ShowCurrentState("Of Shader");
|
||||
state.manager->Clear();
|
||||
} else {
|
||||
auto characteristics = std::make_unique<ShaderCharacteristics>();
|
||||
characteristics->start = start_address;
|
||||
characteristics->settings.depth = settings.depth;
|
||||
characteristics->manager = std::move(manager);
|
||||
characteristics->end = state.block_info.back().end + 1;
|
||||
return characteristics;
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth = settings.depth;
|
||||
result_out.manager = std::move(manager);
|
||||
result_out.end = state.block_info.back().end + 1;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
|
||||
result_out->start = start_address;
|
||||
result_out->settings.depth =
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth =
|
||||
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
||||
result_out->blocks.clear();
|
||||
result_out.blocks.clear();
|
||||
for (auto& block : state.block_info) {
|
||||
ShaderBlock new_block{};
|
||||
new_block.start = block.start;
|
||||
@@ -726,20 +765,20 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
if (!new_block.ignore_branch) {
|
||||
new_block.branch = block.branch;
|
||||
}
|
||||
result_out->end = std::max(result_out->end, block.end);
|
||||
result_out->blocks.push_back(new_block);
|
||||
result_out.end = std::max(result_out.end, block.end);
|
||||
result_out.blocks.push_back(new_block);
|
||||
}
|
||||
if (!use_flow_stack) {
|
||||
result_out->labels = std::move(state.labels);
|
||||
result_out.labels = std::move(state.labels);
|
||||
return result_out;
|
||||
}
|
||||
|
||||
auto back = result_out->blocks.begin();
|
||||
auto back = result_out.blocks.begin();
|
||||
auto next = std::next(back);
|
||||
while (next != result_out->blocks.end()) {
|
||||
while (next != result_out.blocks.end()) {
|
||||
if (!state.labels.contains(next->start) && next->start == back->end + 1) {
|
||||
back->end = next->end;
|
||||
next = result_out->blocks.erase(next);
|
||||
next = result_out.blocks.erase(next);
|
||||
continue;
|
||||
}
|
||||
back = next;
|
||||
@@ -748,4 +787,22 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
return result_out;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings, Registry& registry) {
|
||||
ProgramControl control{};
|
||||
auto result_out = std::make_unique<ShaderProgram>();
|
||||
result_out->main =
|
||||
ScanFunction(control, program_code, start_address, start_address, settings, registry);
|
||||
while (!control.pending_functions.empty()) {
|
||||
u32 address = control.pending_functions.front();
|
||||
auto fun = ScanFunction(control, program_code, address, start_address, settings, registry);
|
||||
result_out->subfunctions.emplace(address, std::move(fun));
|
||||
control.pending_functions.pop_front();
|
||||
}
|
||||
return result_out;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
@@ -101,7 +102,7 @@ struct ShaderBlock {
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderCharacteristics {
|
||||
struct ShaderFunction {
|
||||
std::list<ShaderBlock> blocks{};
|
||||
std::set<u32> labels{};
|
||||
u32 start{};
|
||||
@@ -110,8 +111,12 @@ struct ShaderCharacteristics {
|
||||
CompilerSettings settings{};
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry);
|
||||
struct ShaderProgram {
|
||||
ShaderFunction main;
|
||||
std::map<u32, ShaderFunction> subfunctions;
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings, Registry& registry);
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -64,9 +64,52 @@ std::optional<u32> TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce,
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class ExprDecoder {
|
||||
public:
|
||||
explicit ExprDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||
|
||||
void operator()(const ExprAnd& expr) {
|
||||
Visit(expr.operand1);
|
||||
Visit(expr.operand2);
|
||||
}
|
||||
|
||||
void operator()(const ExprOr& expr) {
|
||||
Visit(expr.operand1);
|
||||
Visit(expr.operand2);
|
||||
}
|
||||
|
||||
void operator()(const ExprNot& expr) {
|
||||
Visit(expr.operand1);
|
||||
}
|
||||
|
||||
void operator()(const ExprPredicate& expr) {
|
||||
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
||||
if (pred != Pred::UnusedIndex && pred != Pred::NeverExecute) {
|
||||
ir.used_predicates.insert(pred);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const ExprCondCode& expr) {}
|
||||
|
||||
void operator()(const ExprVar& expr) {}
|
||||
|
||||
void operator()(const ExprBoolean& expr) {}
|
||||
|
||||
void operator()(const ExprGprEqual& expr) {
|
||||
ir.used_registers.insert(expr.gpr);
|
||||
}
|
||||
|
||||
void Visit(const Expr& node) {
|
||||
return std::visit(*this, *node);
|
||||
}
|
||||
|
||||
private:
|
||||
ShaderIR& ir;
|
||||
};
|
||||
|
||||
class ASTDecoder {
|
||||
public:
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_), decoder(ir_) {}
|
||||
|
||||
void operator()(ASTProgram& ast) {
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
@@ -77,6 +120,7 @@ public:
|
||||
}
|
||||
|
||||
void operator()(ASTIfThen& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -96,13 +140,18 @@ public:
|
||||
|
||||
void operator()(ASTBlockDecoded& ast) {}
|
||||
|
||||
void operator()(ASTVarSet& ast) {}
|
||||
void operator()(ASTVarSet& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTLabel& ast) {}
|
||||
|
||||
void operator()(ASTGoto& ast) {}
|
||||
void operator()(ASTGoto& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTDoWhile& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -110,9 +159,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(ASTReturn& ast) {}
|
||||
void operator()(ASTReturn& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTBreak& ast) {}
|
||||
void operator()(ASTBreak& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void Visit(ASTNode& node) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
@@ -125,77 +178,113 @@ public:
|
||||
|
||||
private:
|
||||
ShaderIR& ir;
|
||||
ExprDecoder decoder;
|
||||
};
|
||||
|
||||
void ShaderIR::Decode() {
|
||||
const auto decode_function = ([this](ShaderFunction& shader_info) {
|
||||
coverage_end = std::max<u32>(0, shader_info.end);
|
||||
switch (shader_info.settings.depth) {
|
||||
case CompileDepth::FlowStack: {
|
||||
for (const auto& block : shader_info.blocks) {
|
||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CompileDepth::NoFlowStack: {
|
||||
disable_flow_stack = true;
|
||||
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||
if (label == static_cast<u32>(exit_branch)) {
|
||||
return;
|
||||
}
|
||||
basic_blocks.insert({label, nodes});
|
||||
};
|
||||
const auto& blocks = shader_info.blocks;
|
||||
NodeBlock current_block;
|
||||
u32 current_label = static_cast<u32>(exit_branch);
|
||||
for (const auto& block : blocks) {
|
||||
if (shader_info.labels.contains(block.start)) {
|
||||
insert_block(current_block, current_label);
|
||||
current_block.clear();
|
||||
current_label = block.start;
|
||||
}
|
||||
if (!block.ignore_branch) {
|
||||
DecodeRangeInner(current_block, block.start, block.end);
|
||||
InsertControlFlow(current_block, block);
|
||||
} else {
|
||||
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||
}
|
||||
}
|
||||
insert_block(current_block, current_label);
|
||||
break;
|
||||
}
|
||||
case CompileDepth::DecompileBackwards:
|
||||
case CompileDepth::FullDecompile: {
|
||||
program_manager = std::move(shader_info.manager);
|
||||
disable_flow_stack = true;
|
||||
decompiled = true;
|
||||
ASTDecoder decoder{*this};
|
||||
ASTNode program = program_manager.GetProgram();
|
||||
decoder.Visit(program);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||
[[fallthrough]];
|
||||
case CompileDepth::BruteForce: {
|
||||
const auto shader_end = static_cast<u32>(program_code.size());
|
||||
coverage_begin = main_offset;
|
||||
coverage_end = shader_end;
|
||||
for (u32 label = main_offset; label < shader_end; ++label) {
|
||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (settings.depth != shader_info.settings.depth) {
|
||||
LOG_WARNING(
|
||||
HW_GPU,
|
||||
"Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||
CompileDepthAsString(settings.depth),
|
||||
CompileDepthAsString(shader_info.settings.depth));
|
||||
}
|
||||
});
|
||||
const auto gen_function =
|
||||
([this](ShaderFunction& shader_info, u32 id) -> std::shared_ptr<ShaderFunctionIR> {
|
||||
std::shared_ptr<ShaderFunctionIR> result;
|
||||
if (decompiled) {
|
||||
result = std::make_shared<ShaderFunctionIR>(std::move(program_manager), id,
|
||||
shader_info.start, shader_info.end);
|
||||
} else {
|
||||
result =
|
||||
std::make_shared<ShaderFunctionIR>(std::move(basic_blocks), disable_flow_stack,
|
||||
id, shader_info.start, shader_info.end);
|
||||
}
|
||||
decompiled = false;
|
||||
disable_flow_stack = false;
|
||||
basic_blocks.clear();
|
||||
program_manager.Clear();
|
||||
return result;
|
||||
});
|
||||
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
||||
|
||||
decompiled = false;
|
||||
auto info = ScanFlow(program_code, main_offset, settings, registry);
|
||||
auto& shader_info = *info;
|
||||
coverage_begin = shader_info.start;
|
||||
coverage_end = shader_info.end;
|
||||
switch (shader_info.settings.depth) {
|
||||
case CompileDepth::FlowStack: {
|
||||
for (const auto& block : shader_info.blocks) {
|
||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||
}
|
||||
break;
|
||||
u32 id_start = 1;
|
||||
for (auto& pair : info->subfunctions) {
|
||||
func_map.emplace(pair.first, id_start);
|
||||
id_start++;
|
||||
}
|
||||
case CompileDepth::NoFlowStack: {
|
||||
disable_flow_stack = true;
|
||||
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||
if (label == static_cast<u32>(exit_branch)) {
|
||||
return;
|
||||
}
|
||||
basic_blocks.insert({label, nodes});
|
||||
};
|
||||
const auto& blocks = shader_info.blocks;
|
||||
NodeBlock current_block;
|
||||
u32 current_label = static_cast<u32>(exit_branch);
|
||||
for (const auto& block : blocks) {
|
||||
if (shader_info.labels.contains(block.start)) {
|
||||
insert_block(current_block, current_label);
|
||||
current_block.clear();
|
||||
current_label = block.start;
|
||||
}
|
||||
if (!block.ignore_branch) {
|
||||
DecodeRangeInner(current_block, block.start, block.end);
|
||||
InsertControlFlow(current_block, block);
|
||||
} else {
|
||||
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||
}
|
||||
}
|
||||
insert_block(current_block, current_label);
|
||||
break;
|
||||
}
|
||||
case CompileDepth::DecompileBackwards:
|
||||
case CompileDepth::FullDecompile: {
|
||||
program_manager = std::move(shader_info.manager);
|
||||
disable_flow_stack = true;
|
||||
decompiled = true;
|
||||
ASTDecoder decoder{*this};
|
||||
ASTNode program = GetASTProgram();
|
||||
decoder.Visit(program);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||
[[fallthrough]];
|
||||
case CompileDepth::BruteForce: {
|
||||
const auto shader_end = static_cast<u32>(program_code.size());
|
||||
coverage_begin = main_offset;
|
||||
coverage_end = shader_end;
|
||||
for (u32 label = main_offset; label < shader_end; ++label) {
|
||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (settings.depth != shader_info.settings.depth) {
|
||||
LOG_WARNING(
|
||||
HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||
CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth));
|
||||
coverage_begin = info->main.start;
|
||||
coverage_end = 0;
|
||||
decode_function(info->main);
|
||||
main_function = gen_function(info->main, 0);
|
||||
subfunctions.resize(info->subfunctions.size());
|
||||
for (auto& pair : info->subfunctions) {
|
||||
auto& func_info = pair.second;
|
||||
decode_function(func_info);
|
||||
u32 id = func_map[pair.first];
|
||||
subfunctions[id - 1] = gen_function(func_info, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
// With the previous preconditions, this instruction is a no-operation.
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const ConditionCode cc = instr.flow_condition_code;
|
||||
UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc);
|
||||
@@ -312,6 +313,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::CAL: {
|
||||
const u32 target = pc + instr.bra.GetBranchTarget();
|
||||
const auto it = func_map.find(target);
|
||||
if (it == func_map.end()) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
bb.push_back(FunctionCall(it->second));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||
}
|
||||
|
||||
@@ -267,10 +267,11 @@ class PatchNode;
|
||||
class SmemNode;
|
||||
class GmemNode;
|
||||
class CommentNode;
|
||||
class FunctionCallNode;
|
||||
|
||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
||||
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
||||
LmemNode, SmemNode, GmemNode, CommentNode>;
|
||||
LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>;
|
||||
using Node = std::shared_ptr<NodeData>;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
@@ -494,6 +495,18 @@ private:
|
||||
std::vector<Node> code; ///< Code to execute
|
||||
};
|
||||
|
||||
class FunctionCallNode final : public AmendNode {
|
||||
public:
|
||||
explicit FunctionCallNode(u32 func_id_) : func_id{func_id_} {}
|
||||
|
||||
[[nodiscard]] u32 GetFuncId() const {
|
||||
return func_id;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 func_id; ///< Id of the function to call
|
||||
};
|
||||
|
||||
/// A general purpose register
|
||||
class GprNode final {
|
||||
public:
|
||||
|
||||
@@ -19,6 +19,11 @@ Node Comment(std::string text) {
|
||||
return MakeNode<CommentNode>(std::move(text));
|
||||
}
|
||||
|
||||
/// Creates a function call
|
||||
Node FunctionCall(u32 func_id) {
|
||||
return MakeNode<FunctionCallNode>(func_id);
|
||||
}
|
||||
|
||||
Node Immediate(u32 value) {
|
||||
return MakeNode<ImmediateNode>(value);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ Node Conditional(Node condition, std::vector<Node> code);
|
||||
/// Creates a commentary node
|
||||
Node Comment(std::string text);
|
||||
|
||||
/// Creates a function call
|
||||
Node FunctionCall(u32 func_id);
|
||||
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace VideoCommon::Shader {
|
||||
|
||||
struct ShaderBlock;
|
||||
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x2000;
|
||||
|
||||
struct ConstBuffer {
|
||||
constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_)
|
||||
@@ -64,16 +64,68 @@ struct GlobalMemoryUsage {
|
||||
bool is_written{};
|
||||
};
|
||||
|
||||
class ShaderFunctionIR final {
|
||||
public:
|
||||
explicit ShaderFunctionIR(std::map<u32, NodeBlock>&& basic_blocks_, bool disable_flow_stack_,
|
||||
u32 id_, u32 coverage_begin_, u32 coverage_end_)
|
||||
: basic_blocks{std::move(basic_blocks_)}, decompiled{false},
|
||||
disable_flow_stack{disable_flow_stack_}, id{id_}, coverage_begin{coverage_begin_},
|
||||
coverage_end{coverage_end_} {}
|
||||
explicit ShaderFunctionIR(ASTManager&& program_manager_, u32 id_, u32 coverage_begin_,
|
||||
u32 coverage_end_)
|
||||
: program_manager{std::move(program_manager_)}, decompiled{true}, disable_flow_stack{true},
|
||||
id{id_}, coverage_begin{coverage_begin_}, coverage_end{coverage_end_} {}
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsFlowStackDisabled() const {
|
||||
return disable_flow_stack;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDecompiled() const {
|
||||
return decompiled;
|
||||
}
|
||||
|
||||
const ASTManager& GetASTManager() const {
|
||||
return program_manager;
|
||||
}
|
||||
|
||||
[[nodiscard]] ASTNode GetASTProgram() const {
|
||||
return program_manager.GetProgram();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetASTNumVariables() const {
|
||||
return program_manager.GetVariables();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsMain() const {
|
||||
return id == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<u32, NodeBlock> basic_blocks;
|
||||
ASTManager program_manager{true, true};
|
||||
|
||||
bool decompiled{};
|
||||
bool disable_flow_stack{};
|
||||
u32 id{};
|
||||
|
||||
u32 coverage_begin{};
|
||||
u32 coverage_end{};
|
||||
};
|
||||
|
||||
class ShaderIR final {
|
||||
public:
|
||||
explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_,
|
||||
CompilerSettings settings_, Registry& registry_);
|
||||
~ShaderIR();
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
}
|
||||
|
||||
const std::set<u32>& GetRegisters() const {
|
||||
return used_registers;
|
||||
}
|
||||
@@ -155,26 +207,6 @@ public:
|
||||
return header;
|
||||
}
|
||||
|
||||
bool IsFlowStackDisabled() const {
|
||||
return disable_flow_stack;
|
||||
}
|
||||
|
||||
bool IsDecompiled() const {
|
||||
return decompiled;
|
||||
}
|
||||
|
||||
const ASTManager& GetASTManager() const {
|
||||
return program_manager;
|
||||
}
|
||||
|
||||
ASTNode GetASTProgram() const {
|
||||
return program_manager.GetProgram();
|
||||
}
|
||||
|
||||
u32 GetASTNumVariables() const {
|
||||
return program_manager.GetVariables();
|
||||
}
|
||||
|
||||
u32 ConvertAddressToNvidiaSpace(u32 address) const {
|
||||
return (address - main_offset) * static_cast<u32>(sizeof(Tegra::Shader::Instruction));
|
||||
}
|
||||
@@ -190,7 +222,16 @@ public:
|
||||
return num_custom_variables;
|
||||
}
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> GetMainFunction() const {
|
||||
return main_function;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<ShaderFunctionIR>>& GetSubFunctions() const {
|
||||
return subfunctions;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ExprDecoder;
|
||||
friend class ASTDecoder;
|
||||
|
||||
struct SamplerInfo {
|
||||
@@ -453,6 +494,10 @@ private:
|
||||
std::vector<Node> amend_code;
|
||||
u32 num_custom_variables{};
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> main_function;
|
||||
std::vector<std::shared_ptr<ShaderFunctionIR>> subfunctions;
|
||||
std::unordered_map<u32, u32> func_map;
|
||||
|
||||
std::set<u32> used_registers;
|
||||
std::set<Tegra::Shader::Pred> used_predicates;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
|
||||
Reference in New Issue
Block a user