Skate 3: gameplay-context plumbing, one-shot boot auto-tap, emulated draw suppression
This commit is contained in:
parent
c4b0dfe2c9
commit
c09b8c892d
6 changed files with 90 additions and 5 deletions
|
|
@ -86,4 +86,12 @@ using NativeGuestOutputRenderer = bool (*)(const NativeGuestOutputRenderContext&
|
|||
void SetNativeGuestOutputRenderer(NativeGuestOutputRenderer renderer, void* user_data);
|
||||
bool TryRenderNativeGuestOutput(const NativeGuestOutputRenderContext& context);
|
||||
|
||||
// True while the registered renderer actually replaced the last presented
|
||||
// frame (false when it yields to the emulated output).
|
||||
bool IsNativeGuestOutputActive();
|
||||
// native_render_suppress_emulated_draws && IsNativeGuestOutputActive():
|
||||
// command processors skip emulated draw/resolve execution (memexport draws,
|
||||
// fences, queries and PM4 parsing still run).
|
||||
bool ShouldSuppressEmulatedDraws();
|
||||
|
||||
} // namespace rex::graphics
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@ enum class ApplyMode : uint32_t {
|
|||
uint64_t HashKey(const TargetKey& key);
|
||||
void NotifySkate3GameplayContext(uint32_t user_index, uint32_t context_id, uint32_t value);
|
||||
bool IsSkate3GameplayUltrawideActive();
|
||||
// Live value of the Skate 3 presence context 0x8001 (1 = in gameplay,
|
||||
// 0 = frontend / pause menu / loading).
|
||||
uint32_t Skate3GameplayContextValue();
|
||||
bool ShouldApplyAndRecord(const TargetKey& key, bool default_enabled);
|
||||
uint32_t ShadowMapCandidateLevel(const TargetKey& key);
|
||||
bool IsShadowMapCandidate(const TargetKey& key, uint32_t mode = 1);
|
||||
|
|
|
|||
|
|
@ -723,10 +723,19 @@ std::string CurrentGuestFunctionStack() {
|
|||
|
||||
} // namespace
|
||||
|
||||
// Live value of the Skate 3 presence context (0x8001): 1 = in gameplay,
|
||||
// 0 = frontend / pause menu / loading. Consumers (the native scene renderer)
|
||||
// yield to the emulated output while the game is in menus.
|
||||
static std::atomic<uint32_t> g_skate3_gameplay_context_value{0};
|
||||
|
||||
void NotifySkate3GameplayContext(uint32_t user_index, uint32_t context_id, uint32_t value) {
|
||||
constexpr uint32_t kSkate3GameplayContext = 0x8001;
|
||||
constexpr uint32_t kSkate3GameplayValue = 1;
|
||||
if (user_index != 0 || context_id != kSkate3GameplayContext || value != kSkate3GameplayValue) {
|
||||
if (user_index != 0 || context_id != kSkate3GameplayContext) {
|
||||
return;
|
||||
}
|
||||
g_skate3_gameplay_context_value.store(value, std::memory_order_relaxed);
|
||||
if (value != kSkate3GameplayValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -740,6 +749,10 @@ bool IsSkate3GameplayUltrawideActive() {
|
|||
return g_skate3_gameplay_ultrawide_latched.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
uint32_t Skate3GameplayContextValue() {
|
||||
return g_skate3_gameplay_context_value.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
GuestFunctionScope::GuestFunctionScope(const char* function_name) {
|
||||
if (!IsGuestStoreWatchActive()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2770,6 +2770,13 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, uint3
|
|||
PROFILE_DRAW_FINGERPRINT(fingerprint);
|
||||
}
|
||||
#endif
|
||||
if (ShouldSuppressEmulatedDraws()) {
|
||||
// Native output active: skip the resolve. Suppressed draws leave only
|
||||
// garbage in the EDRAM buffer; copying it out would overwrite guest
|
||||
// texture payloads (e.g. previously composed CAS textures) that the
|
||||
// native renderer still samples.
|
||||
return true;
|
||||
}
|
||||
return IssueCopy();
|
||||
}
|
||||
|
||||
|
|
@ -2816,6 +2823,16 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, uint3
|
|||
bool memexport_used_pixel = pixel_shader && (pixel_shader->memexport_eM_written() != 0);
|
||||
bool memexport_used = memexport_used_vertex || memexport_used_pixel;
|
||||
|
||||
// Native guest-output renderer active: the emulated frame is never shown,
|
||||
// so skip the draw entirely (pipeline setup, texture cache, render target
|
||||
// cache, GPU work). Memexport draws still execute; the game reads their
|
||||
// results back from memory. Fences, queries and PM4 processing are
|
||||
// unaffected. Menus/pause flip the native renderer inactive, so emulated
|
||||
// rendering (and CAS texture composition) works normally there.
|
||||
if (!memexport_used && ShouldSuppressEmulatedDraws()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!BeginSubmission(true)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,26 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
|
||||
// Defined here (shared TU) rather than in a backend's command_processor.cpp
|
||||
// so both D3D12 and Vulkan see the same definition.
|
||||
REXCVAR_DEFINE_BOOL(native_render_suppress_emulated_draws, false, "GPU",
|
||||
"While the registered native guest-output renderer is actively "
|
||||
"replacing frames, skip emulated draw and resolve execution in the "
|
||||
"command processor (PM4 parsing, fences, queries and memexport draws "
|
||||
"still run). Menus/pause yield to the emulated path and execute "
|
||||
"normally.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
|
||||
namespace rex::graphics {
|
||||
namespace {
|
||||
|
||||
std::atomic<NativeGuestOutputRenderer> g_renderer{nullptr};
|
||||
std::atomic<void*> g_renderer_user_data{nullptr};
|
||||
// True while the registered renderer actually replaced the last presented
|
||||
// frame (false when it yields: menus, early-outs, no renderer).
|
||||
std::atomic<bool> g_native_output_active{false};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
@ -18,10 +33,22 @@ void SetNativeGuestOutputRenderer(NativeGuestOutputRenderer renderer, void* user
|
|||
bool TryRenderNativeGuestOutput(const NativeGuestOutputRenderContext& context) {
|
||||
NativeGuestOutputRenderer renderer = g_renderer.load(std::memory_order_acquire);
|
||||
if (renderer == nullptr) {
|
||||
g_native_output_active.store(false, std::memory_order_relaxed);
|
||||
return false;
|
||||
}
|
||||
void* user_data = g_renderer_user_data.load(std::memory_order_acquire);
|
||||
return renderer(context, user_data);
|
||||
const bool rendered = renderer(context, user_data);
|
||||
g_native_output_active.store(rendered, std::memory_order_relaxed);
|
||||
return rendered;
|
||||
}
|
||||
|
||||
bool IsNativeGuestOutputActive() {
|
||||
return g_native_output_active.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool ShouldSuppressEmulatedDraws() {
|
||||
return REXCVAR_GET(native_render_suppress_emulated_draws) &&
|
||||
g_native_output_active.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace rex::graphics
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
*/
|
||||
|
||||
#include <rex/kernel/xam/apps/xgi_app.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
#include <rex/graphics/ultrawide_debug.h>
|
||||
#include <rex/input/input.h>
|
||||
|
|
@ -50,12 +53,26 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
|||
context_value);
|
||||
if (rex::cvar::Query<bool>("skate3_demo_path") && user_index == 0 &&
|
||||
context_id == 0x8001) {
|
||||
// Boot automation is strictly ONE-SHOT: the dialog A auto-tap exists
|
||||
// only to accept boot-flow dialogs (autosave prompt etc.) between the
|
||||
// press-start screen and gameplay. The game also sets this context to
|
||||
// 0 for the PAUSE MENU and loading screens; re-arming the auto-tap
|
||||
// there blindly "confirmed" pause menu entries and teleported the
|
||||
// player across the map. Once gameplay (context 1) has been seen,
|
||||
// never inject input again for the rest of the session.
|
||||
static std::atomic<bool> boot_automation_done{false};
|
||||
if (context_value == 0) {
|
||||
rex::kernel::xam::SetSyntheticAutoTap(rex::input::X_INPUT_GAMEPAD_A, true);
|
||||
REXKRNL_INFO("Skate 3 demo path: gameplay context 0; enabling dialog A auto-tap");
|
||||
if (!boot_automation_done.load(std::memory_order_relaxed)) {
|
||||
rex::kernel::xam::SetSyntheticAutoTap(rex::input::X_INPUT_GAMEPAD_A, true);
|
||||
REXKRNL_INFO("Skate 3 demo path: gameplay context 0; enabling dialog A auto-tap");
|
||||
}
|
||||
} else if (context_value == 1) {
|
||||
rex::kernel::xam::ClearSyntheticInput();
|
||||
REXKRNL_INFO("Skate 3 demo path: gameplay context 1; stopping synthetic input");
|
||||
if (!boot_automation_done.exchange(true, std::memory_order_relaxed)) {
|
||||
REXKRNL_INFO(
|
||||
"Skate 3 demo path: gameplay reached; boot automation complete, synthetic "
|
||||
"input disabled for the session");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!old_value || *old_value != context_value) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue