diff options
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 42 | ||||
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 16 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/gpu.h | 3 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_info.cpp | 15 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 6 |
6 files changed, 78 insertions, 8 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h index b7f0e9261a..fc7de1fae7 100644 --- a/src/core/hle/service/nx2_gsv.h +++ b/src/core/hle/service/nx2_gsv.h @@ -21,6 +21,7 @@ #include "common/alignment.h" #include "common/settings.h" #include "core/core.h" +#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" #include "core/frontend/framebuffer_layout.h" #include "core/perf_stats.h" @@ -758,6 +759,7 @@ private: } system.GPU().RequestComposite(std::move(layers), std::move(fences)); CaptureFrame(); + DumpRanges(); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().EndSystemFrame(); system.GetPerfStats().BeginSystemFrame(); @@ -797,6 +799,46 @@ private: layout); } + // EDEN_NX2_DUMP=<present>:<hex address>:<hex size>[,<hex address>:<hex size>...]: at that + // present, flush those GPU ranges back from the host GPU and save their bytes as + // nx2-dump-<address>.bin in the EDEN_NX2_FRAME_CAPTURE directory (to inspect what a pass + // rendered, e.g. a 3D volume, offline). + void DumpRanges() { + static const std::string spec = [] { + const char* text = std::getenv("EDEN_NX2_DUMP"); + return text ? std::string{text} : std::string{}; + }(); + if (spec.empty() || m_presents != std::strtoull(spec.c_str(), nullptr, 10)) { + return; + } + const char* const directory = std::getenv("EDEN_NX2_FRAME_CAPTURE"); + size_t position = spec.find(':'); + while (position != std::string::npos) { + char* end{}; + const u64 address = std::strtoull(spec.c_str() + position + 1, &end, 16); + const u64 size = *end == ':' ? std::strtoull(end + 1, &end, 16) : 0; + position = spec.find(',', position + 1); // the next "address:size" + const std::string path = fmt::format("{}/nx2-dump-{:x}.bin", directory ? directory : ".", address); + // On the GPU thread: waiting for it here can deadlock with a GPU wait on this present. + static_cast<void>(system.GPU().RunOnGpuThread([this, address, size, path] { + std::vector<u8> bytes(size); + for (u64 current = address; current < address + size;) { + const u64 chunk = std::min((current | 0xfff) + 1, address + size) - current; + if (const auto device = Nvidia::Devices::Nx2GpuMemoryRegistry::Get().DeviceAddressOf(current)) { + system.GPU().Renderer().ReadRasterizer()->FlushRegion(*device, chunk); + system.Host1x().MemoryManager().ReadBlock(*device, bytes.data() + (current - address), chunk); + } + current += chunk; + } + if (std::FILE* file = std::fopen(path.c_str(), "wb")) { + std::fwrite(bytes.data(), 1, bytes.size(), file); + std::fclose(file); + } + LOG_INFO(Service, "nx2: dumped {:#x} bytes at {:#x} to {}", size, address, path); + })); + } + } + // 228(u64 token, u32 slot): return a buffer without presenting it. void CancelClientBuffer(HLERequestContext& ctx) { constexpr u64 AvailableSlots = 0x7fff; diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 0cebaa4363..1390951ef5 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -161,15 +161,17 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { }; std::string_view operand_error; const auto emit = [&](u64 word) { result.instructions.push_back(word); }; - const u32 guard_mask = 1U << guard_slot; - const auto save_predicate = [&] { - emit(0x38e8000000070000ULL | 253 | (u64{255} << 8) | (u64{guard_mask} << 20)); + // P(guard slot) while a uniform guard is swapped in; P6 for lowerings that use it as + // scratch (R2UR, MATCH, HFMA2.RELU), which save and restore it with the defaults below. + const u32 guard_mask = guard_slot < 7 ? 1U << guard_slot : 0U; + const auto save_predicate = [&](u32 mask = 1U << 6) { + emit(0x38e8000000070000ULL | 253 | (u64{255} << 8) | (u64{mask} << 20)); }; - const auto restore_predicate = [&] { - emit(0x38f0000000070000ULL | (u64{253} << 8) | (u64{guard_mask} << 20)); + const auto restore_predicate = [&](u32 mask = 1U << 6) { + emit(0x38f0000000070000ULL | (u64{253} << 8) | (u64{mask} << 20)); }; if (guarded_uniform) { - save_predicate(); + save_predicate(guard_mask); emit(0x38f0000000070000ULL | (u64{239} << 8) | (u64{guard_mask} << 20)); } const u32 output_mask = uniform_output == 7 ? 0U : 1U << (6 - uniform_output); @@ -2229,7 +2231,7 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { emit(0x38e8000000070000ULL | 239 | (u64{239} << 8) | (u64{output_mask} << 20)); emit(0x38f0000000070000ULL | (u64{254} << 8) | (u64{output_mask} << 20)); } - if (guarded_uniform) restore_predicate(); + if (guarded_uniform) restore_predicate(guard_mask); } return fail(static_cast<u32>(code.size()), main_exited ? "native program lacks captured library return" : "native program has no unconditional EXIT"); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 2a61a66d82..6441b9511a 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -411,6 +411,10 @@ u64 GPU::RequestFlush(DAddr addr, std::size_t size) { }); } +u64 GPU::RunOnGpuThread(std::function<void()>&& action) { + return impl->RequestSyncOperation(std::move(action)); +} + u64 GPU::CurrentSyncRequestFence() const { return impl->CurrentSyncRequestFence(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 41ba186cd3..3a6546b41b 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -164,6 +164,9 @@ public: /// Request a host GPU memory flush from the CPU. [[nodiscard]] u64 RequestFlush(DAddr addr, std::size_t size); + /// Runs an action on the GPU thread between command lists (returns its sync fence). + u64 RunOnGpuThread(std::function<void()>&& action); + /// Obtains current flush request fence id. [[nodiscard]] u64 CurrentSyncRequestFence() const; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 8b59eb6841..7e86bf00cf 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -4,9 +4,13 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> +#include <array> +#include <vector> #include <fmt/ranges.h> #include "common/assert.h" +#include "common/logging.h" #include "common/settings.h" #include "video_core/surface.h" #include "video_core/texture_cache/format_lookup_table.h" @@ -109,14 +113,23 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { size.height = config.Height(); resources.layers = config.BaseLayer() + config.Depth() * 6; break; - case TextureType::Texture3D: + case TextureType::Texture3D: { ASSERT(config.BaseLayer() == 0); type = ImageType::e3D; size.width = config.Width(); size.height = config.Height(); size.depth = config.Depth(); resources.layers = 1; + // A few distinct 3D descriptors, raw (to check Switch 2 volume fields). + static std::vector<std::array<u64, 4>> logged; + if (logged.size() < 16 && std::find(logged.begin(), logged.end(), config.raw) == logged.end()) { + logged.push_back(config.raw); + LOG_INFO(HW_GPU, "3D TIC {:016x} {:016x} {:016x} {:016x}: {}x{}x{} block_depth {} levels {}", + config.raw[0], config.raw[1], config.raw[2], config.raw[3], size.width, size.height, + size.depth, config.block_depth.Value(), config.max_mip_level.Value() + 1); + } break; + } case TextureType::Texture1DBuffer: type = ImageType::Buffer; size.width = config.Width(); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f3da5684a5..75005f0731 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1569,6 +1569,12 @@ ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr={:#x}", gpu_addr); ImageInfo storage_info = info; storage_info.native_tiling = gpu_memory->UsesNativeTiling(); + if (storage_info.native_tiling && info.type == ImageType::e3D) { + // Switch 2 3D images (lighting/fog volumes) are rare: log each one's shape. + LOG_INFO(HW_GPU, "3D image at {:#x}: {}x{}x{} levels {} format {} block {}x{}x{}", + gpu_addr, info.size.width, info.size.height, info.size.depth, info.resources.levels, + static_cast<u32>(info.format), info.block.width, info.block.height, info.block.depth); + } const ImageId image_id = JoinImages(storage_info, gpu_addr, *cpu_addr); const Image& image = slot_images[image_id]; // Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different |
