diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/shader_recompiler/ir_opt/position_pass.cpp | 7 | ||||
| -rw-r--r-- | src/video_core/engines/kepler_compute.cpp | 55 | ||||
| -rw-r--r-- | src/video_core/engines/kepler_compute.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 7 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 85 |
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(); |
