early-access version 1358

This commit is contained in:
pineappleEA 2021-01-23 07:41:16 +01:00
parent 2e0deb5e50
commit 8231512517
13 changed files with 199 additions and 293 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1357. This is the source code for early-access 1358.
## Legal Notice ## Legal Notice

View file

@ -402,6 +402,8 @@ add_library(core STATIC
hle/service/ldr/ldr.h hle/service/ldr/ldr.h
hle/service/lm/lm.cpp hle/service/lm/lm.cpp
hle/service/lm/lm.h hle/service/lm/lm.h
hle/service/lm/manager.cpp
hle/service/lm/manager.h
hle/service/mig/mig.cpp hle/service/mig/mig.cpp
hle/service/mig/mig.h hle/service/mig/mig.h
hle/service/mii/manager.cpp hle/service/mii/manager.cpp

View file

@ -36,6 +36,7 @@
#include "core/hle/service/apm/controller.h" #include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h" #include "core/hle/service/glue/manager.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_manager.h"
@ -292,6 +293,8 @@ struct System::Impl {
perf_stats->GetMeanFrametime()); perf_stats->GetMeanFrametime());
} }
lm_manager.Flush();
is_powered_on = false; is_powered_on = false;
exit_lock = false; exit_lock = false;
@ -395,6 +398,7 @@ struct System::Impl {
/// Service State /// Service State
Service::Glue::ARPManager arp_manager; Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
Service::Time::TimeManager time_manager; Service::Time::TimeManager time_manager;
/// Service manager /// Service manager
@ -716,6 +720,14 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller; return impl->apm_controller;
} }
Service::LM::Manager& System::GetLogManager() {
return impl->lm_manager;
}
const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
Service::Time::TimeManager& System::GetTimeManager() { Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager; return impl->time_manager;
} }

View file

@ -62,6 +62,10 @@ namespace Glue {
class ARPManager; class ARPManager;
} }
namespace LM {
class Manager;
} // namespace LM
namespace SM { namespace SM {
class ServiceManager; class ServiceManager;
} // namespace SM } // namespace SM
@ -347,6 +351,9 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController(); [[nodiscard]] Service::APM::Controller& GetAPMController();
[[nodiscard]] const Service::APM::Controller& GetAPMController() const; [[nodiscard]] const Service::APM::Controller& GetAPMController() const;
[[nodiscard]] Service::LM::Manager& GetLogManager();
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager(); [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;

View file

@ -5,71 +5,22 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <optional>
#include <unordered_map>
#include <boost/container_hash/hash.hpp>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h" #include "core/hle/service/lm/lm.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/memory.h" #include "core/memory.h"
namespace Service::LM { namespace Service::LM {
enum class LogSeverity : u8 {
Trace = 0,
Info = 1,
Warning = 2,
Error = 3,
Fatal = 4,
};
// To keep flags out of hashing as well as the payload size
struct LogPacketHeaderEntry {
u64_le pid{};
u64_le tid{};
LogSeverity severity{};
u8 verbosity{};
auto operator<=>(const LogPacketHeaderEntry&) const = default;
};
} // namespace Service::LM
namespace std {
template <>
struct hash<Service::LM::LogPacketHeaderEntry> {
std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
std::size_t seed{};
boost::hash_combine(seed, k.pid);
boost::hash_combine(seed, k.tid);
boost::hash_combine(seed, k.severity);
boost::hash_combine(seed, k.verbosity);
return seed;
};
};
} // namespace std
namespace Service::LM {
enum class LogDestination : u32 {
TargetManager = 1 << 0,
Uart = 1 << 1,
UartSleep = 1 << 2,
All = 0xffff,
};
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
enum class LogPacketFlags : u8 {
Head = 1 << 0,
Tail = 1 << 1,
LittleEndian = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
class ILogger final : public ServiceFramework<ILogger> { class ILogger final : public ServiceFramework<ILogger> {
public: public:
explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} { explicit ILogger(Core::System& system_)
: ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
memory{system_.Memory()} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ILogger::Log, "Log"}, {0, &ILogger::Log, "Log"},
{1, &ILogger::SetDestination, "SetDestination"}, {1, &ILogger::SetDestination, "SetDestination"},
@ -79,260 +30,54 @@ public:
private: private:
void Log(Kernel::HLERequestContext& ctx) { void Log(Kernel::HLERequestContext& ctx) {
std::size_t offset{};
const auto data = ctx.ReadBuffer();
// This function only succeeds - Get that out of the way // This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
if (data.size() < sizeof(LogPacketHeader)) { // Read MessageHeader, despite not doing anything with it right now
LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size()); MessageHeader header{};
return; VAddr addr{ctx.BufferDescriptorX()[0].Address()};
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
memory.ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
FieldMap fields;
while (addr < end_addr) {
const auto field = static_cast<Field>(memory.Read8(addr++));
const auto length = memory.Read8(addr++);
if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
++addr;
} }
LogPacketHeader header{}; SCOPE_EXIT({ addr += length; });
std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
offset += sizeof(LogPacketHeader);
LogPacketHeaderEntry entry{ if (field == Field::Skip) {
.pid = header.pid, continue;
.tid = header.tid,
.severity = header.severity,
.verbosity = header.verbosity,
};
if (True(header.flags & LogPacketFlags::Head)) {
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
std::memcpy(tmp.data(), data.data() + offset, tmp.size());
entries[entry] = std::move(tmp);
} else {
// Append to existing entry
if (!entries.contains(entry)) {
LOG_ERROR(Service_LM, "Log entry does not exist!");
return;
}
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
auto& existing_entry = entries[entry];
const auto base = existing_entry.size();
existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
std::memcpy(existing_entry.data() + base, data.data() + offset,
(data.size() - sizeof(LogPacketHeader)));
} }
if (True(header.flags & LogPacketFlags::Tail)) { std::vector<u8> data(length);
auto it = entries.find(entry); memory.ReadBlock(addr, data.data(), length);
if (it == entries.end()) { fields.emplace(field, std::move(data));
LOG_ERROR(Service_LM, "Log entry does not exist!");
return;
}
ParseLog(it->first, it->second);
entries.erase(it);
} }
manager.Log({header, std::move(fields)});
} }
void SetDestination(Kernel::HLERequestContext& ctx) { void SetDestination(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto log_destination = rp.PopEnum<LogDestination>(); const auto destination = rp.PopEnum<DestinationFlag>();
LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination)); LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
destination = log_destination;
manager.SetDestination(destination);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) { Manager& manager;
u32 result{}; Core::Memory::Memory& memory;
u32 shift{};
do {
result |= (data[offset] & 0x7f) << shift;
shift += 7;
offset++;
if (offset >= data.size()) {
break;
}
} while ((data[offset] & 0x80) != 0);
return result;
}
std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
std::size_t length) {
if (length == 0) {
return std::nullopt;
}
std::string output(length, '\0');
std::memcpy(output.data(), data.data() + offset, length);
offset += length;
return output;
}
u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
ASSERT(length == sizeof(u32));
u32_le output{};
std::memcpy(&output, data.data() + offset, sizeof(u32));
offset += length;
return output;
}
u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
ASSERT(length == sizeof(u64));
u64_le output{};
std::memcpy(&output, data.data() + offset, sizeof(u64));
offset += length;
return output;
}
void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
// Possible entries
std::optional<std::string> text_log;
std::optional<u32> line_number;
std::optional<std::string> file_name;
std::optional<std::string> function_name;
std::optional<std::string> module_name;
std::optional<std::string> thread_name;
std::optional<u64> log_pack_drop_count;
std::optional<s64> user_system_clock;
std::optional<std::string> process_name;
std::size_t offset{};
while (offset < log_data.size()) {
const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
const auto chunk_size = ReadLeb128(log_data, offset);
switch (key) {
case LogDataChunkKey::LogSessionBegin:
case LogDataChunkKey::LogSessionEnd:
break;
case LogDataChunkKey::TextLog:
text_log = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::LineNumber:
line_number = ReadAsU32(log_data, offset, chunk_size);
break;
case LogDataChunkKey::FileName:
file_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::FunctionName:
function_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ModuleName:
module_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ThreadName:
thread_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::LogPacketDropCount:
log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
break;
case LogDataChunkKey::UserSystemClock:
user_system_clock = ReadAsU64(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ProcessName:
process_name = ReadString(log_data, offset, chunk_size);
break;
}
}
std::string output_log{};
if (process_name) {
output_log += fmt::format("Process: {}\n", *process_name);
}
if (module_name) {
output_log += fmt::format("Module: {}\n", *module_name);
}
if (file_name) {
output_log += fmt::format("File: {}\n", *file_name);
}
if (function_name) {
output_log += fmt::format("Function: {}\n", *function_name);
}
if (line_number && *line_number != 0) {
output_log += fmt::format("Line: {}\n", *line_number);
}
output_log += fmt::format("ProcessID: {}\n", entry.pid);
output_log += fmt::format("ThreadID: {}\n", entry.tid);
if (text_log) {
output_log += fmt::format("Log Text: {}\n", *text_log);
}
switch (entry.severity) {
case LogSeverity::Trace:
LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Info:
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Warning:
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
DestinationToString(destination), output_log);
break;
case LogSeverity::Error:
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Fatal:
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
output_log);
break;
default:
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
DestinationToString(destination), output_log);
break;
}
}
static std::string DestinationToString(LogDestination destination) {
if (True(destination & LogDestination::All)) {
return "TargetManager | Uart | UartSleep";
}
std::string output{};
if (True(destination & LogDestination::TargetManager)) {
output += "| TargetManager";
}
if (True(destination & LogDestination::Uart)) {
output += "| Uart";
}
if (True(destination & LogDestination::UartSleep)) {
output += "| UartSleep";
}
if (output.length() > 0) {
return output.substr(2);
}
return "No Destination";
}
enum class LogDataChunkKey : u32 {
LogSessionBegin = 0,
LogSessionEnd = 1,
TextLog = 2,
LineNumber = 3,
FileName = 4,
FunctionName = 5,
ModuleName = 6,
ThreadName = 7,
LogPacketDropCount = 8,
UserSystemClock = 9,
ProcessName = 10,
};
struct LogPacketHeader {
u64_le pid{};
u64_le tid{};
LogPacketFlags flags{};
INSERT_PADDING_BYTES(1);
LogSeverity severity{};
u8 verbosity{};
u32_le payload_size{};
};
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
LogDestination destination{LogDestination::All};
}; };
class LM final : public ServiceFramework<LM> { class LM final : public ServiceFramework<LM> {

View file

@ -20,6 +20,7 @@
#include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/lm/manager.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/reporter.h" #include "core/reporter.h"
#include "core/settings.h" #include "core/settings.h"
@ -359,6 +360,55 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
} }
void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const {
if (!IsReportingEnabled()) {
return;
}
const auto timestamp = GetTimestamp();
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] =
GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp);
out["log_destination"] =
fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination));
auto json_messages = json::array();
std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages),
[](const Service::LM::LogMessage& message) {
json out;
out["is_head"] = fmt::format("{}", message.header.IsHeadLog());
out["is_tail"] = fmt::format("{}", message.header.IsTailLog());
out["pid"] = fmt::format("{:016X}", message.header.pid);
out["thread_context"] =
fmt::format("{:016X}", message.header.thread_context);
out["payload_size"] = fmt::format("{:016X}", message.header.payload_size);
out["flags"] = fmt::format("{:04X}", message.header.flags.Value());
out["severity"] = fmt::format("{}", message.header.severity.Value());
out["verbosity"] = fmt::format("{:02X}", message.header.verbosity);
auto fields = json::array();
std::transform(message.fields.begin(), message.fields.end(),
std::back_inserter(fields), [](const auto& kv) {
json out;
out["type"] = fmt::format("{}", kv.first);
out["data"] =
Service::LM::FormatField(kv.first, kv.second);
return out;
});
out["fields"] = std::move(fields);
return out;
});
out["log_messages"] = std::move(json_messages);
SaveToFile(std::move(out),
GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp));
}
void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
std::string log_message) const { std::string log_message) const {
if (!IsReportingEnabled()) if (!IsReportingEnabled())

View file

@ -72,6 +72,9 @@ public:
void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
std::string log_message) const; std::string log_message) const;
// Used by lm services
void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const;
// Can be used anywhere to generate a backtrace and general info report at any point during // Can be used anywhere to generate a backtrace and general info report at any point during
// execution. Not intended to be used for anything other than debugging or testing. // execution. Not intended to be used for anything other than debugging or testing.
void SaveUserReport() const; void SaveUserReport() const;

View file

@ -1243,9 +1243,15 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
if (!cpu_addr || size == 0) { if (!cpu_addr || size == 0) {
return NULL_BINDING; return NULL_BINDING;
} }
// HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range.
// It exists due to some games like Astral Chain operate out of bounds.
// Binding the whole map range would be technically correct, but games have large maps that make
// this approach unaffordable for now.
static constexpr u32 arbitrary_extra_bytes = 0xc000;
const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr));
const Binding binding{ const Binding binding{
.cpu_addr = *cpu_addr, .cpu_addr = *cpu_addr,
.size = size, .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end),
.buffer_id = BufferId{}, .buffer_id = BufferId{},
}; };
return binding; return binding;

View file

@ -4,6 +4,7 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
@ -38,6 +39,12 @@ GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std
} }
GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
if (it != map_ranges.end() && it->first == gpu_addr) {
it->second = size;
} else {
map_ranges.insert(it, MapRange{gpu_addr, size});
}
return UpdateRange(gpu_addr, cpu_addr, size); return UpdateRange(gpu_addr, cpu_addr, size);
} }
@ -52,10 +59,16 @@ GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
} }
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
if (!size) { if (size == 0) {
return; return;
} }
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
if (it != map_ranges.end()) {
ASSERT(it->first == gpu_addr);
map_ranges.erase(it);
} else {
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
}
// Flush and invalidate through the GPU interface, to be asynchronous if possible. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr); const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr); ASSERT(cpu_addr);
@ -237,6 +250,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
return system.Memory().GetPointer(*address); return system.Memory().GetPointer(*address);
} }
size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
--it;
return it->second - (gpu_addr - it->first);
}
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
std::size_t remaining_size{size}; std::size_t remaining_size{size};
std::size_t page_index{gpu_src_addr >> page_bits}; std::size_t page_index{gpu_src_addr >> page_bits};

View file

@ -85,6 +85,9 @@ public:
[[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] u8* GetPointer(GPUVAddr addr);
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
/// Returns the number of bytes until the end of the memory map containing the given GPU address
[[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept;
/** /**
* ReadBlock and WriteBlock are full read and write operations over virtual * ReadBlock and WriteBlock are full read and write operations over virtual
* GPU Memory. It's important to use these when GPU memory may not be continuous * GPU Memory. It's important to use these when GPU memory may not be continuous
@ -160,6 +163,9 @@ private:
std::vector<PageEntry> page_table; std::vector<PageEntry> page_table;
std::vector<std::pair<VAddr, std::size_t>> cache_invalidate_queue; std::vector<std::pair<VAddr, std::size_t>> cache_invalidate_queue;
using MapRange = std::pair<GPUVAddr, size_t>;
std::vector<MapRange> map_ranges;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -465,6 +465,14 @@ public:
return operands.size(); return operands.size();
} }
NodeBlock& GetOperands() {
return operands;
}
const NodeBlock& GetOperands() const {
return operands;
}
[[nodiscard]] const Node& operator[](std::size_t operand_index) const { [[nodiscard]] const Node& operator[](std::size_t operand_index) const {
return operands.at(operand_index); return operands.at(operand_index);
} }

View file

@ -388,9 +388,54 @@ void ShaderIR::SetInternalFlagsFromInteger(NodeBlock& bb, Node value, bool sets_
if (!sets_cc) { if (!sets_cc) {
return; return;
} }
switch (value->index()) {
case 0: // Operation Node
SearchOperands(bb, value);
break;
case 2: // Genral Purpose Node
if (const auto* gpr = std::get_if<GprNode>(value.get())) {
LOG_DEBUG(HW_GPU, "GprNode: index={}", gpr->GetIndex());
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value),
Immediate(gpr->GetIndex()));
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
}
break;
default:
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0)); Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0));
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete"); LOG_WARNING(HW_GPU, "Node Type: {}", value->index());
break;
}
}
void ShaderIR::SearchOperands(NodeBlock& nb, Node var) {
const auto* op = std::get_if<OperationNode>(var.get());
if (op == nullptr) {
return;
}
if (op->GetOperandsCount() == 0) {
return;
}
for (auto& operand : op->GetOperands()) {
switch (operand->index()) {
case 0: // Operation Node
return SearchOperands(nb, operand);
case 2: // General Purpose Node
if (const auto* gpr = std::get_if<GprNode>(operand.get())) {
LOG_DEBUG(HW_GPU, "Child GprNode: index={}", gpr->GetIndex());
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(operand),
Immediate(gpr->GetIndex()));
SetInternalFlag(nb, InternalFlag::Zero, std::move(zerop));
}
break;
default:
LOG_WARNING(HW_GPU, "Child Node Type: {}", operand->index());
break;
}
}
} }
Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) { Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) {

View file

@ -350,6 +350,9 @@ private:
/// Access a bindless image sampler. /// Access a bindless image sampler.
ImageEntry& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); ImageEntry& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
/// Recursive Iteration over the OperationNode operands, searching for GprNodes.
void SearchOperands(NodeBlock& nb, Node var);
/// Extracts a sequence of bits from a node /// Extracts a sequence of bits from a node
Node BitfieldExtract(Node value, u32 offset, u32 bits); Node BitfieldExtract(Node value, u32 offset, u32 bits);