aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 02:45:00 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 02:45:00 -0400
commit3e48eedecf91f8f8d0917f8f78ce26a3d0482e5c (patch)
tree5a5b4bf2932b7ea0d7467aa8916a37d73600aceb /src/video_core/renderer_vulkan
parent585e457172693730fe82f34a2ce513ac7cdc948d (diff)
nx2: title 4's cutscene scene renders; blit constants for fragment shaders
- Render area falls back to colour target 0 when the surface clip is unset (Switch 2 titles may never program it); it feeds the synthesized blit block. - BlitConstantsPass also covers fragment shaders: UV clamp rectangle 0..1 (0x30-0x3c) and zero dither bias. - Debug: EDEN_NX2_SKIP_VS=<addr,...>, EDEN_NX2_TRACE_VS=<addr>, EDEN_NX2_UPSCALE_SOURCE[_FORMAT], EDEN_NX2_TRACE_AFTER_UPSCALE, EDEN_NX2_SAMPLE_AT, skipped-draw reasons, clear tracing. Showing the raw HDR scene colour through the upscale fallback gives the correct lit cockpit scene; the game's tonemap pass still outputs black.
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp85
2 files changed, 89 insertions, 3 deletions
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();