aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/ir_opt/position_pass.cpp7
-rw-r--r--src/video_core/engines/kepler_compute.cpp55
-rw-r--r--src/video_core/engines/kepler_compute.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp85
5 files changed, 148 insertions, 10 deletions
diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp
index e68e175abc..4890346944 100644
--- a/src/shader_recompiler/ir_opt/position_pass.cpp
+++ b/src/shader_recompiler/ir_opt/position_pass.cpp
@@ -77,9 +77,10 @@ void PositionPass(Environment& env, IR::Program& program) {
// Switch 2 fullscreen blit vertex shaders read a parameter block from constant bank 24 (lowered to
// bank 17, which games never bind) that the console provides itself. For a blit covering the whole
// render target it holds: texel size (0x0/0x4, again at 0x10/0x14), size (0x8/0xc), a UV scale of
-// 1 (0x18/0x1c) and the integer source rectangle 0,0..W-1,H-1 (0x50-0x5c).
+// 1 (0x18/0x1c), the UV clamp rectangle 0,0..1,1 (0x30-0x3c, read by the fragment shader), a zero
+// dither bias (0x40-0x48) and the integer source rectangle 0,0..W-1,H-1 (0x50-0x5c).
void BlitConstantsPass(Environment& env, IR::Program& program) {
- if (env.ShaderStage() != Stage::VertexB) {
+ if (env.ShaderStage() != Stage::VertexB && env.ShaderStage() != Stage::Fragment) {
return;
}
bool used{};
@@ -110,6 +111,8 @@ void BlitConstantsPass(Environment& env, IR::Program& program) {
return ir.BitCast<IR::U32>(height);
case 0x18:
case 0x1c:
+ case 0x38:
+ case 0x3c:
return ir.Imm32(0x3f800000U);
case 0x58:
return IR::U32{ir.ConvertFToU(32, ir.FPAdd(width, ir.Imm32(-1.0f)))};
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 136fcb9dda..85b1f971f7 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <atomic>
#include <bitset>
#include <cstdlib>
#include <fstream>
@@ -432,6 +433,8 @@ void KeplerCompute::DescribeNativeResources(const NativeQmdScheduler::Descriptor
}
}
+std::atomic<u32> g_nx2_trace_draws_after_upscale{};
+
// Title 4's upscaler reads the tonemapped frame through the texture handle at cbuf0+0x90 and
// writes the output resolution image through the surface handle at cbuf0+0xa0 (from its
// TLD/SUST operands). Blit one to the other with bilinear filtering when both are plain 2D images
@@ -486,7 +489,11 @@ bool KeplerCompute::UpscaleInsteadOfNativeProgram() {
.src_y1 = static_cast<s32>(input->Height()),
};
// Debug: does the input hold anything? Download it once and count non-zero words.
- if (static u32 sampled{}; sampled++ == 20) {
+ static const u32 sample_at = [] {
+ const char* value = std::getenv("EDEN_NX2_SAMPLE_AT");
+ return value ? static_cast<u32>(std::strtoul(value, nullptr, 10)) : 20U;
+ }();
+ if (static u32 sampled{}; sampled++ == sample_at) {
const auto sample = [&](const Texture::TICEntry& tic) {
const u64 size = u64{tic.Width()} * tic.Height() * 4;
if (const auto device = memory_manager.GpuToCpuAddress(tic.Address())) {
@@ -527,12 +534,50 @@ bool KeplerCompute::UpscaleInsteadOfNativeProgram() {
(void)fake;
}
}
- const bool done = rasterizer->AccelerateSurfaceCopy(surface(*input), surface(*output), config);
+ auto source = surface(*input);
+ // EDEN_NX2_UPSCALE_SOURCE=<gpu address>: blit from another same-sized image (debug aid).
+ if (static const u64 override_source = [] {
+ const char* value = std::getenv("EDEN_NX2_UPSCALE_SOURCE");
+ return value ? std::strtoull(value, nullptr, 16) : 0ULL;
+ }();
+ override_source != 0) {
+ source.addr_upper = static_cast<u32>(override_source >> 32);
+ source.addr_lower = static_cast<u32>(override_source);
+ if (const char* value = std::getenv("EDEN_NX2_UPSCALE_SOURCE_FORMAT")) {
+ source.format = static_cast<RTFormat>(std::strtoul(value, nullptr, 16));
+ }
+ }
+ const bool done = rasterizer->AccelerateSurfaceCopy(source, surface(*output), config);
+ if (static u32 checked{}; checked++ == sample_at) {
+ const u64 size = u64{output->Width()} * output->Height() * 4;
+ if (const auto device = memory_manager.GpuToCpuAddress(output->Address())) {
+ rasterizer->FlushRegion(*device, size, VideoCommon::CacheType::All);
+ }
+ u32 nonzero{}, samples{}, changes{}, last{};
+ for (u64 offset = 0; offset < size; offset += 4096 + 4) {
+ const u32 word = memory_manager.Read<u32>(output->Address() + offset);
+ ++samples;
+ nonzero += word != 0;
+ changes += word != last;
+ last = word;
+ }
+ LOG_INFO(HW_GPU, "Upscale fallback output after blit: {}/{} non-zero, {} changes", nonzero, samples,
+ changes);
+ }
+ // Debug: trace the draws that follow one upscale (EDEN_NX2_RT_TRACE_AFTER_UPSCALE).
+ static const u32 trace_at = [] {
+ const char* value = std::getenv("EDEN_NX2_TRACE_AFTER_UPSCALE");
+ return value ? static_cast<u32>(std::strtoul(value, nullptr, 10)) : 30U;
+ }();
+ if (static u32 count{}; ++count == trace_at) g_nx2_trace_draws_after_upscale = 40;
static u32 logged{};
if (logged++ < 4) {
- LOG_INFO(HW_GPU, "Upscale fallback {:#x} {}x{} -> {:#x} {}x{}: {}", input->Address(),
- input->Width(), input->Height(), output->Address(), output->Width(),
- output->Height(), done ? "blitted" : "not accelerated");
+ LOG_INFO(HW_GPU, "Upscale fallback {:#x} {}x{} block {}/{}/{} -> {:#x} {}x{} block {}/{}/{} levels {}: {}",
+ input->Address(), input->Width(), input->Height(), input->block_width.Value(),
+ input->block_height.Value(), input->block_depth.Value(), output->Address(),
+ output->Width(), output->Height(), output->block_width.Value(),
+ output->block_height.Value(), output->block_depth.Value(), output->max_mip_level.Value(),
+ done ? "blitted" : "not accelerated");
}
return done;
}
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 3bd41a2dfa..dd69211f7b 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -7,6 +7,7 @@
#pragma once
#include <array>
+#include <atomic>
#include <cstddef>
#include <optional>
#include <vector>
@@ -33,6 +34,9 @@ class RasterizerInterface;
namespace Tegra::Engines {
+/// Draws left to trace after an upscale fallback (debug aid).
+extern std::atomic<u32> g_nx2_trace_draws_after_upscale;
+
/**
* This Engine is known as GK104_Compute. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_compute.xml
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 8b5b0bc9c5..172ee4dcaa 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -529,8 +529,11 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto& info{stage_infos[stage]};
if (info.uses_render_area) {
render_area.uses_render_area = true;
- render_area.words = {static_cast<float>(regs.surface_clip.width),
- static_cast<float>(regs.surface_clip.height)};
+ // Switch 2 titles may never program the surface clip; use colour target 0 then.
+ const u32 clip_width = regs.surface_clip.width, clip_height = regs.surface_clip.height;
+ const bool clip_set = clip_width != 0 && clip_height != 0;
+ render_area.words = {static_cast<float>(clip_set ? clip_width : u32{regs.rt[0].width}),
+ static_cast<float>(clip_set ? clip_height : u32{regs.rt[0].height})};
}
}};
if constexpr (Spec::enabled_stages[0]) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 47a55c9580..1b05716653 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -256,15 +256,28 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
FlushWork();
gpu_memory->FlushCaching();
+ const auto report_failure = [&](std::string_view reason) {
+ static std::set<std::tuple<u64, u64, std::string_view>> seen;
+ const auto& regs = maxwell3d->regs;
+ const u64 vs = (u64{regs.pipelines[1].reserved[0] & 0xff} << 32) | regs.pipelines[1].reserved[1];
+ const u64 ps = (u64{regs.pipelines[5].reserved[0] & 0xff} << 32) | regs.pipelines[5].reserved[1];
+ if (seen.size() < 64 && seen.emplace(vs, ps, reason).second) {
+ LOG_WARNING(HW_GPU, "Draw skipped ({}): vs={:#x} ps={:#x} rt0={:#x} {}x{}", reason, vs, ps,
+ regs.rt[0].Address(), regs.rt[0].width, regs.rt[0].height);
+ }
+ };
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
if (!pipeline) {
+ report_failure("no pipeline");
return;
}
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
// update engine as channel may be different.
pipeline->SetEngine(maxwell3d, gpu_memory);
- if (!pipeline->Configure(is_indexed))
+ if (!pipeline->Configure(is_indexed)) {
+ report_failure("configure failed");
return;
+ }
UpdateDynamicStates();
@@ -428,6 +441,69 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
}
}
}
+ // EDEN_NX2_TRACE_VS=<address>: describe the next few draws using that vertex shader like the
+ // after-upscale trace does (render target, blend, fragment textures).
+ static const u64 trace_vs = [] {
+ const char* value = std::getenv("EDEN_NX2_TRACE_VS");
+ return value ? std::strtoull(value, nullptr, 16) : 0ULL;
+ }();
+ if (trace_vs != 0) {
+ const auto& regs = maxwell3d->regs;
+ const u64 vs = (u64{regs.pipelines[1].reserved[0] & 0xff} << 32) | regs.pipelines[1].reserved[1];
+ static u32 traced{};
+ if (vs == trace_vs && traced++ % 200 == 0 && traced < 1000) {
+ Tegra::Engines::g_nx2_trace_draws_after_upscale = 1;
+ }
+ }
+ if (Tegra::Engines::g_nx2_trace_draws_after_upscale > 0) {
+ --Tegra::Engines::g_nx2_trace_draws_after_upscale;
+ const auto& regs = maxwell3d->regs;
+ const u64 vs = (u64{regs.pipelines[1].reserved[0] & 0xff} << 32) | regs.pipelines[1].reserved[1];
+ const u64 ps = (u64{regs.pipelines[5].reserved[0] & 0xff} << 32) | regs.pipelines[5].reserved[1];
+ LOG_INFO(HW_GPU, "after upscale: draw rt0 {:#x} {}x{} fmt {:#x} block {}/{}/{} pitch_linear {} depth {} array_pitch {:#x} ctrl {:#x} vs {:#x} ps {:#x}",
+ regs.rt[0].Address(), regs.rt[0].width, regs.rt[0].height,
+ static_cast<u32>(regs.rt[0].format), regs.rt[0].tile_mode.block_width.Value(),
+ regs.rt[0].tile_mode.block_height.Value(), regs.rt[0].tile_mode.block_depth.Value(),
+ regs.rt[0].tile_mode.is_pitch_linear.Value(), regs.rt[0].depth.Value(),
+ regs.rt[0].array_pitch, regs.rt_control.count.Value(), vs, ps);
+ // Fragment-stage texture handles (bound buffer 2) and the images they name.
+ const auto& handles = maxwell3d->state.shader_stages[4].const_buffers[2];
+ if (handles.enabled) {
+ for (u32 i = 0; i < 24; ++i) {
+ const u32 handle = gpu_memory->Read<u32>(handles.address + i * 4);
+ if (handle == 0 || handle == 0xffffffff) continue;
+ Tegra::Texture::TICEntry tic{};
+ gpu_memory->ReadBlockUnsafe(regs.tex_header.Address() + u64{handle & 0xfffff} * sizeof(tic),
+ &tic, sizeof(tic));
+ LOG_INFO(HW_GPU, " fs handle[{}] = {:#x}: {:#x} {}x{} format {:#x}", i, handle,
+ tic.Address(), tic.Width(), tic.Height(), static_cast<u32>(tic.format.Value()));
+ }
+ }
+ const auto& bt = regs.blend_per_target[0];
+ LOG_INFO(HW_GPU, " blend enable {} per-target {} global op {} src {:#x} dst {:#x} | rt0 op {} src {:#x} dst {:#x} alpha {:#x}/{:#x} mask {:#x}",
+ regs.blend.enable[0], regs.blend_per_target_enabled,
+ static_cast<u32>(regs.blend.color_op), static_cast<u32>(regs.blend.color_source),
+ static_cast<u32>(regs.blend.color_dest), static_cast<u32>(bt.color_op),
+ static_cast<u32>(bt.color_source), static_cast<u32>(bt.color_dest),
+ static_cast<u32>(bt.alpha_source), static_cast<u32>(bt.alpha_dest),
+ regs.color_mask[0].raw);
+ }
+ // EDEN_NX2_SKIP_VS=<address>[,<address>...]: drop draws using those vertex shaders (debug aid).
+ static const std::vector<u64> skip_list = [] {
+ std::vector<u64> list;
+ if (const char* value = std::getenv("EDEN_NX2_SKIP_VS")) {
+ for (char* end = nullptr; *value; value = *end ? end + 1 : end) {
+ list.push_back(std::strtoull(value, &end, 16));
+ if (end == value) break;
+ }
+ }
+ return list;
+ }();
+ if (!skip_list.empty()) {
+ const auto& regs = maxwell3d->regs;
+ const u64 vs = (u64{regs.pipelines[1].reserved[0] & 0xff} << 32) | regs.pipelines[1].reserved[1];
+ if (std::find(skip_list.begin(), skip_list.end(), vs) != skip_list.end()) return;
+ }
// EDEN_NX2_RT_TRACE: log the first draw to every distinct colour target (debug aid).
if (static const bool trace = std::getenv("EDEN_NX2_RT_TRACE") != nullptr; trace) {
static std::set<std::tuple<GPUVAddr, u32, u32, u64, u64>> seen;
@@ -580,6 +656,13 @@ void RasterizerVulkan::DrawTexture() {
}
void RasterizerVulkan::Clear(u32 layer_count) {
+ if (Tegra::Engines::g_nx2_trace_draws_after_upscale > 0) {
+ const auto& regs = maxwell3d->regs;
+ LOG_INFO(HW_GPU, "after upscale: clear rt0 {:#x} {}x{} fmt {:#x} surface {:#x} colour {} {} {} {}",
+ regs.rt[0].Address(), regs.rt[0].width, regs.rt[0].height,
+ static_cast<u32>(regs.rt[0].format), regs.clear_surface.raw, regs.clear_color[0],
+ regs.clear_color[1], regs.clear_color[2], regs.clear_color[3]);
+ }
TraceWindowTarget(*maxwell3d, *gpu_memory, "clear", layer_count);
FlushWork();
gpu_memory->FlushCaching();