aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/nx2_gsv.h42
-rw-r--r--src/shader_recompiler/frontend/ampere/lower_to_maxwell.h16
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/video_core/texture_cache/image_info.cpp15
-rw-r--r--src/video_core/texture_cache/texture_cache.h6
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