From 82315125174d6258ffb0decfba3e5a5dd32f7fae Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Sat, 23 Jan 2021 07:41:16 +0100 Subject: [PATCH] early-access version 1358 --- README.md | 2 +- src/core/CMakeLists.txt | 2 + src/core/core.cpp | 12 + src/core/core.h | 7 + src/core/hle/service/lm/lm.cpp | 317 ++------------------- src/core/reporter.cpp | 50 ++++ src/core/reporter.h | 3 + src/video_core/buffer_cache/buffer_cache.h | 8 +- src/video_core/memory_manager.cpp | 23 +- src/video_core/memory_manager.h | 6 + src/video_core/shader/node.h | 8 + src/video_core/shader/shader_ir.cpp | 51 +++- src/video_core/shader/shader_ir.h | 3 + 13 files changed, 199 insertions(+), 293 deletions(-) diff --git a/README.md b/README.md index 49ce91167..eedd1f9c6 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1357. +This is the source code for early-access 1358. ## Legal Notice diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 848b7ca7c..9f2c7cada 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -402,6 +402,8 @@ add_library(core STATIC hle/service/ldr/ldr.h hle/service/lm/lm.cpp hle/service/lm/lm.h + hle/service/lm/manager.cpp + hle/service/lm/manager.h hle/service/mig/mig.cpp hle/service/mig/mig.h hle/service/mii/manager.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 30f5e1128..bf7851438 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -36,6 +36,7 @@ #include "core/hle/service/apm/controller.h" #include "core/hle/service/filesystem/filesystem.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/sm/sm.h" #include "core/hle/service/time/time_manager.h" @@ -292,6 +293,8 @@ struct System::Impl { perf_stats->GetMeanFrametime()); } + lm_manager.Flush(); + is_powered_on = false; exit_lock = false; @@ -395,6 +398,7 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; + Service::LM::Manager lm_manager{reporter}; Service::Time::TimeManager time_manager; /// Service manager @@ -716,6 +720,14 @@ const Service::APM::Controller& System::GetAPMController() const { 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() { return impl->time_manager; } diff --git a/src/core/core.h b/src/core/core.h index 3a8e040c1..579a774e4 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -62,6 +62,10 @@ namespace Glue { class ARPManager; } +namespace LM { +class Manager; +} // namespace LM + namespace SM { class ServiceManager; } // namespace SM @@ -347,6 +351,9 @@ public: [[nodiscard]] Service::APM::Controller& GetAPMController(); [[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]] const Service::Time::TimeManager& GetTimeManager() const; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 4e0b4ea09..8e49b068c 100755 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -5,71 +5,22 @@ #include #include -#include -#include -#include #include "common/logging/log.h" #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/lm/lm.h" +#include "core/hle/service/lm/manager.h" #include "core/hle/service/service.h" #include "core/memory.h" 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 { - 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 { 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[] = { {0, &ILogger::Log, "Log"}, {1, &ILogger::SetDestination, "SetDestination"}, @@ -79,260 +30,54 @@ public: private: void Log(Kernel::HLERequestContext& ctx) { - std::size_t offset{}; - const auto data = ctx.ReadBuffer(); - // This function only succeeds - Get that out of the way IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - if (data.size() < sizeof(LogPacketHeader)) { - LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size()); - return; - } + // Read MessageHeader, despite not doing anything with it right now + MessageHeader header{}; + VAddr addr{ctx.BufferDescriptorX()[0].Address()}; + const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; + memory.ReadBlock(addr, &header, sizeof(MessageHeader)); + addr += sizeof(MessageHeader); - LogPacketHeader header{}; - std::memcpy(&header, data.data(), sizeof(LogPacketHeader)); - offset += sizeof(LogPacketHeader); + FieldMap fields; + while (addr < end_addr) { + const auto field = static_cast(memory.Read8(addr++)); + const auto length = memory.Read8(addr++); - LogPacketHeaderEntry entry{ - .pid = header.pid, - .tid = header.tid, - .severity = header.severity, - .verbosity = header.verbosity, - }; - - if (True(header.flags & LogPacketFlags::Head)) { - std::vector 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; + if (static_cast(memory.Read8(addr)) == Field::Skip) { + ++addr; } - std::vector 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))); - } + SCOPE_EXIT({ addr += length; }); - if (True(header.flags & LogPacketFlags::Tail)) { - auto it = entries.find(entry); - if (it == entries.end()) { - LOG_ERROR(Service_LM, "Log entry does not exist!"); - return; + if (field == Field::Skip) { + continue; } - ParseLog(it->first, it->second); - entries.erase(it); + + std::vector data(length); + memory.ReadBlock(addr, data.data(), length); + fields.emplace(field, std::move(data)); } + + manager.Log({header, std::move(fields)}); } void SetDestination(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto log_destination = rp.PopEnum(); + const auto destination = rp.PopEnum(); - LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination)); - destination = log_destination; + LOG_DEBUG(Service_LM, "called, destination={:08X}", destination); + + manager.SetDestination(destination); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - u32 ReadLeb128(const std::vector& data, std::size_t& offset) { - u32 result{}; - 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 ReadString(const std::vector& 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& 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& 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& log_data) { - // Possible entries - std::optional text_log; - std::optional line_number; - std::optional file_name; - std::optional function_name; - std::optional module_name; - std::optional thread_name; - std::optional log_pack_drop_count; - std::optional user_system_clock; - std::optional process_name; - - std::size_t offset{}; - while (offset < log_data.size()) { - const auto key = static_cast(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> entries{}; - LogDestination destination{LogDestination::All}; + Manager& manager; + Core::Memory::Memory& memory; }; class LM final : public ServiceFramework { diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index f199c3362..0becdf642 100755 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -20,6 +20,7 @@ #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" +#include "core/hle/service/lm/manager.h" #include "core/memory.h" #include "core/reporter.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)); } +void Reporter::SaveLogReport(u32 destination, std::vector 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(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, std::string log_message) const { if (!IsReportingEnabled()) diff --git a/src/core/reporter.h b/src/core/reporter.h index b2c2d9a2e..86d760cf0 100755 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -72,6 +72,9 @@ public: void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, std::string log_message) const; + // Used by lm services + void SaveLogReport(u32 destination, std::vector messages) const; + // 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. void SaveUserReport() const; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index a296036f4..2a6844ab1 100755 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1243,9 +1243,15 @@ typename BufferCache

::Binding BufferCache

::StorageBufferBinding(GPUVAddr s if (!cpu_addr || size == 0) { 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(gpu_memory.BytesToMapEnd(gpu_addr)); const Binding binding{ .cpu_addr = *cpu_addr, - .size = size, + .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end), .buffer_id = BufferId{}, }; return binding; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index fed2a5ea0..01e2d3e6a 100755 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -4,6 +4,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/memory/page_table.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) { + 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); } @@ -52,10 +59,16 @@ GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { - if (!size) { + if (size == 0) { 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. const std::optional cpu_addr = GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); @@ -237,6 +250,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { 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 { std::size_t remaining_size{size}; std::size_t page_index{gpu_src_addr >> page_bits}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 1e9f49393..b8357bd46 100755 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -85,6 +85,9 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[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 * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -160,6 +163,9 @@ private: std::vector page_table; std::vector> cache_invalidate_queue; + + using MapRange = std::pair; + std::vector map_ranges; }; } // namespace Tegra diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index b54d33763..c9840b75e 100755 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -465,6 +465,14 @@ public: return operands.size(); } + NodeBlock& GetOperands() { + return operands; + } + + const NodeBlock& GetOperands() const { + return operands; + } + [[nodiscard]] const Node& operator[](std::size_t operand_index) const { return operands.at(operand_index); } diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index a4987ffc6..caf5ff362 100755 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -388,9 +388,54 @@ void ShaderIR::SetInternalFlagsFromInteger(NodeBlock& bb, Node value, bool sets_ if (!sets_cc) { return; } - Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0)); - SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); - LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete"); + switch (value->index()) { + case 0: // Operation Node + SearchOperands(bb, value); + break; + case 2: // Genral Purpose Node + if (const auto* gpr = std::get_if(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)); + SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); + LOG_WARNING(HW_GPU, "Node Type: {}", value->index()); + break; + } +} + +void ShaderIR::SearchOperands(NodeBlock& nb, Node var) { + const auto* op = std::get_if(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(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) { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 1cd7c14d7..0928620bb 100755 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -350,6 +350,9 @@ private: /// Access a bindless image sampler. 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 Node BitfieldExtract(Node value, u32 offset, u32 bits);