mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 03:18:55 +02:00
Compare commits
16 commits
d06eb3f52f
...
842acf2c86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842acf2c86 | ||
|
|
2d4c460011 | ||
|
|
c42c67ab85 | ||
|
|
5bce1993e1 | ||
|
|
8c79a3a8de | ||
|
|
ebb3cda782 | ||
|
|
daf972b517 | ||
|
|
cd25e61afa | ||
|
|
4a7aa1618a | ||
|
|
1aae75512b | ||
|
|
825b899bf8 | ||
|
|
73c16c3d45 | ||
|
|
124c97a88a | ||
|
|
02f9c5ffd2 | ||
|
|
042a10cf71 | ||
|
|
d6a889828f |
23 changed files with 247 additions and 479 deletions
|
|
@ -647,15 +647,6 @@ abstract class SettingsItem(
|
|||
valuesId = R.array.dmaAccuracyValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.FRAME_PACING_MODE,
|
||||
titleId = R.string.frame_pacing_mode,
|
||||
descriptionId = R.string.frame_pacing_mode_description,
|
||||
choicesId = R.array.framePacingModeNames,
|
||||
valuesId = R.array.framePacingModeValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
|
||||
|
|
|
|||
|
|
@ -267,7 +267,6 @@ class SettingsFragmentPresenter(
|
|||
|
||||
add(IntSetting.RENDERER_ACCURACY.key)
|
||||
add(IntSetting.DMA_ACCURACY.key)
|
||||
add(IntSetting.FRAME_PACING_MODE.key)
|
||||
add(IntSetting.MAX_ANISOTROPY.key)
|
||||
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
|
||||
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
|
||||
|
|
|
|||
|
|
@ -94,8 +94,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
|
||||
// Since Android 15, google automatically forces "games" to be 60 hrz
|
||||
// This ensures the display's max refresh rate is actually used
|
||||
display?.let {
|
||||
val supportedModes = it.supportedModes
|
||||
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }
|
||||
|
|
|
|||
|
|
@ -6,8 +6,15 @@
|
|||
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/drivers/android.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
|
|
@ -22,6 +29,12 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
|||
m_window_width = 0;
|
||||
m_window_height = 0;
|
||||
window_info.render_surface = nullptr;
|
||||
m_last_frame_rate_hint = -1.0f;
|
||||
m_pending_frame_rate_hint = -1.0f;
|
||||
m_pending_frame_rate_hint_votes = 0;
|
||||
m_smoothed_present_rate = 0.0f;
|
||||
m_last_frame_display_time = {};
|
||||
m_pending_frame_rate_since = {};
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +45,7 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
|||
UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
|
||||
|
||||
window_info.render_surface = reinterpret_cast<void*>(surface);
|
||||
UpdateFrameRateHint();
|
||||
}
|
||||
|
||||
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
|
||||
|
|
@ -51,6 +65,9 @@ void EmuWindow_Android::OnTouchReleased(int id) {
|
|||
}
|
||||
|
||||
void EmuWindow_Android::OnFrameDisplayed() {
|
||||
UpdateObservedFrameRate();
|
||||
UpdateFrameRateHint();
|
||||
|
||||
if (!m_first_frame) {
|
||||
Common::Android::RunJNIOnFiber<void>(
|
||||
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
|
||||
|
|
@ -58,6 +75,166 @@ void EmuWindow_Android::OnFrameDisplayed() {
|
|||
}
|
||||
}
|
||||
|
||||
void EmuWindow_Android::UpdateObservedFrameRate() {
|
||||
const auto now = Clock::now();
|
||||
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
|
||||
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
|
||||
const float seconds = frame_time.count();
|
||||
if (seconds > 0.0f) {
|
||||
const float instantaneous_rate = 1.0f / seconds;
|
||||
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
|
||||
instantaneous_rate <= 240.0f) {
|
||||
constexpr float SmoothingFactor = 0.15f;
|
||||
if (m_smoothed_present_rate <= 0.0f) {
|
||||
m_smoothed_present_rate = instantaneous_rate;
|
||||
} else {
|
||||
m_smoothed_present_rate +=
|
||||
(instantaneous_rate - m_smoothed_present_rate) * SmoothingFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_last_frame_display_time = now;
|
||||
}
|
||||
|
||||
float EmuWindow_Android::QuantizeFrameRateHint(float frame_rate) {
|
||||
if (!std::isfinite(frame_rate) || frame_rate <= 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
frame_rate = std::clamp(frame_rate, 1.0f, 240.0f);
|
||||
|
||||
constexpr float Step = 0.5f;
|
||||
return std::round(frame_rate / Step) * Step;
|
||||
}
|
||||
|
||||
float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
|
||||
if (!EmulationSession::GetInstance().IsRunning()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const double frame_time_scale =
|
||||
EmulationSession::GetInstance().System().GetPerfStats().GetLastFrameTimeScale();
|
||||
if (!std::isfinite(frame_time_scale) || frame_time_scale <= 0.0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float verified_rate =
|
||||
std::clamp(60.0f / static_cast<float>(frame_time_scale), 0.0f, 240.0f);
|
||||
return QuantizeFrameRateHint(verified_rate);
|
||||
}
|
||||
|
||||
float EmuWindow_Android::GetFrameRateHint() const {
|
||||
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
|
||||
const float frame_time_verified_hint = GetFrameTimeVerifiedHint();
|
||||
|
||||
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
|
||||
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
|
||||
if (std::fabs(observed_rate - m_last_frame_rate_hint) <= tolerance) {
|
||||
return m_last_frame_rate_hint;
|
||||
}
|
||||
}
|
||||
|
||||
const float observed_hint = QuantizeFrameRateHint(observed_rate);
|
||||
if (observed_hint > 0.0f) {
|
||||
if (frame_time_verified_hint > 0.0f) {
|
||||
const float tolerance = std::max(observed_hint * 0.20f, 3.0f);
|
||||
if (std::fabs(observed_hint - frame_time_verified_hint) <= tolerance) {
|
||||
return QuantizeFrameRateHint((observed_hint + frame_time_verified_hint) * 0.5f);
|
||||
}
|
||||
}
|
||||
return observed_hint;
|
||||
}
|
||||
|
||||
if (frame_time_verified_hint > 0.0f) {
|
||||
return frame_time_verified_hint;
|
||||
}
|
||||
|
||||
constexpr float NominalFrameRate = 60.0f;
|
||||
if (!Settings::values.use_speed_limit.GetValue()) {
|
||||
return NominalFrameRate;
|
||||
}
|
||||
|
||||
const u16 speed_limit = Settings::SpeedLimit();
|
||||
if (speed_limit == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float speed_limited_rate =
|
||||
NominalFrameRate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
|
||||
return QuantizeFrameRateHint(speed_limited_rate);
|
||||
}
|
||||
|
||||
void EmuWindow_Android::UpdateFrameRateHint() {
|
||||
auto* const surface = reinterpret_cast<ANativeWindow*>(window_info.render_surface);
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto now = Clock::now();
|
||||
const float frame_rate_hint = GetFrameRateHint();
|
||||
if (std::fabs(frame_rate_hint - m_last_frame_rate_hint) < 0.01f) {
|
||||
m_pending_frame_rate_hint = frame_rate_hint;
|
||||
m_pending_frame_rate_hint_votes = 0;
|
||||
m_pending_frame_rate_since = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame_rate_hint == 0.0f) {
|
||||
m_pending_frame_rate_hint = frame_rate_hint;
|
||||
m_pending_frame_rate_hint_votes = 0;
|
||||
m_pending_frame_rate_since = now;
|
||||
} else if (m_last_frame_rate_hint >= 0.0f) {
|
||||
if (std::fabs(frame_rate_hint - m_pending_frame_rate_hint) >= 0.01f) {
|
||||
m_pending_frame_rate_hint = frame_rate_hint;
|
||||
m_pending_frame_rate_hint_votes = 1;
|
||||
m_pending_frame_rate_since = now;
|
||||
return;
|
||||
}
|
||||
|
||||
++m_pending_frame_rate_hint_votes;
|
||||
if (m_pending_frame_rate_since.time_since_epoch().count() == 0) {
|
||||
m_pending_frame_rate_since = now;
|
||||
}
|
||||
|
||||
const auto stable_for = now - m_pending_frame_rate_since;
|
||||
const float reference_rate = std::max(frame_rate_hint, 1.0f);
|
||||
const auto stable_duration = std::chrono::duration_cast<Clock::duration>(
|
||||
std::chrono::duration<float>(std::clamp(3.0f / reference_rate, 0.15f, 0.40f)));
|
||||
constexpr std::uint32_t MinStableVotes = 3;
|
||||
|
||||
if (m_pending_frame_rate_hint_votes < MinStableVotes || stable_for < stable_duration) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
m_pending_frame_rate_since = now;
|
||||
}
|
||||
|
||||
using SetFrameRateWithChangeStrategyFn =
|
||||
int32_t (*)(ANativeWindow*, float, int8_t, int8_t);
|
||||
static const auto set_frame_rate_with_change_strategy =
|
||||
reinterpret_cast<SetFrameRateWithChangeStrategyFn>(
|
||||
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRateWithChangeStrategy"));
|
||||
|
||||
if (!set_frame_rate_with_change_strategy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = set_frame_rate_with_change_strategy(
|
||||
surface, frame_rate_hint,
|
||||
static_cast<int8_t>(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
|
||||
static_cast<int8_t>(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS));
|
||||
if (result != 0) {
|
||||
LOG_DEBUG(Frontend, "Failed to update Android surface frame rate hint: {}", result);
|
||||
return;
|
||||
}
|
||||
|
||||
m_last_frame_rate_hint = frame_rate_hint;
|
||||
m_pending_frame_rate_hint = frame_rate_hint;
|
||||
m_pending_frame_rate_hint_votes = 0;
|
||||
m_pending_frame_rate_since = {};
|
||||
}
|
||||
|
||||
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library)
|
||||
: m_driver_library{driver_library} {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
|
|
@ -50,10 +55,24 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
void UpdateFrameRateHint();
|
||||
void UpdateObservedFrameRate();
|
||||
[[nodiscard]] float GetFrameRateHint() const;
|
||||
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
|
||||
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
|
||||
|
||||
float m_window_width{};
|
||||
float m_window_height{};
|
||||
|
||||
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
||||
|
||||
bool m_first_frame = false;
|
||||
float m_last_frame_rate_hint = -1.0f;
|
||||
float m_pending_frame_rate_hint = -1.0f;
|
||||
float m_smoothed_present_rate = 0.0f;
|
||||
Clock::time_point m_last_frame_display_time{};
|
||||
Clock::time_point m_pending_frame_rate_since{};
|
||||
std::uint32_t m_pending_frame_rate_hint_votes = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,159 +11,15 @@
|
|||
#include <vector>
|
||||
#include <spirv-tools/optimizer.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
[[nodiscard]] constexpr std::string_view StageName(Stage stage) noexcept {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
return "VertexA";
|
||||
case Stage::VertexB:
|
||||
return "VertexB";
|
||||
case Stage::TessellationControl:
|
||||
return "TessellationControl";
|
||||
case Stage::TessellationEval:
|
||||
return "TessellationEval";
|
||||
case Stage::Geometry:
|
||||
return "Geometry";
|
||||
case Stage::Fragment:
|
||||
return "Fragment";
|
||||
case Stage::Compute:
|
||||
return "Compute";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view DenormModeName(bool flush, bool preserve) noexcept {
|
||||
if (flush && preserve) {
|
||||
return "Flush+Preserve";
|
||||
}
|
||||
if (flush) {
|
||||
return "Flush";
|
||||
}
|
||||
if (preserve) {
|
||||
return "Preserve";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsFp32RoundingRelevantOpcode(IR::Opcode opcode) noexcept {
|
||||
switch (opcode) {
|
||||
case IR::Opcode::FPAdd32:
|
||||
case IR::Opcode::FPFma32:
|
||||
case IR::Opcode::FPMul32:
|
||||
case IR::Opcode::FPRoundEven32:
|
||||
case IR::Opcode::FPFloor32:
|
||||
case IR::Opcode::FPCeil32:
|
||||
case IR::Opcode::FPTrunc32:
|
||||
case IR::Opcode::FPOrdEqual32:
|
||||
case IR::Opcode::FPUnordEqual32:
|
||||
case IR::Opcode::FPOrdNotEqual32:
|
||||
case IR::Opcode::FPUnordNotEqual32:
|
||||
case IR::Opcode::FPOrdLessThan32:
|
||||
case IR::Opcode::FPUnordLessThan32:
|
||||
case IR::Opcode::FPOrdGreaterThan32:
|
||||
case IR::Opcode::FPUnordGreaterThan32:
|
||||
case IR::Opcode::FPOrdLessThanEqual32:
|
||||
case IR::Opcode::FPUnordLessThanEqual32:
|
||||
case IR::Opcode::FPOrdGreaterThanEqual32:
|
||||
case IR::Opcode::FPUnordGreaterThanEqual32:
|
||||
case IR::Opcode::ConvertF16F32:
|
||||
case IR::Opcode::ConvertF64F32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct Fp32RoundingUsage {
|
||||
u32 rz_count{};
|
||||
bool has_conflicting_rounding{};
|
||||
};
|
||||
|
||||
Fp32RoundingUsage CollectFp32RoundingUsage(const IR::Program& program) {
|
||||
Fp32RoundingUsage usage{};
|
||||
for (const IR::Block* const block : program.post_order_blocks) {
|
||||
for (const IR::Inst& inst : block->Instructions()) {
|
||||
if (!IsFp32RoundingRelevantOpcode(inst.GetOpcode())) {
|
||||
continue;
|
||||
}
|
||||
switch (inst.Flags<IR::FpControl>().rounding) {
|
||||
case IR::FpRounding::RZ:
|
||||
++usage.rz_count;
|
||||
break;
|
||||
case IR::FpRounding::RN:
|
||||
case IR::FpRounding::RM:
|
||||
case IR::FpRounding::RP:
|
||||
usage.has_conflicting_rounding = true;
|
||||
break;
|
||||
case IR::FpRounding::DontCare:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
void LogRzBackendSummary(const Profile& profile, const IR::Program& program, bool optimize) {
|
||||
if (!Settings::values.renderer_debug) {
|
||||
return;
|
||||
}
|
||||
const Fp32RoundingUsage usage{CollectFp32RoundingUsage(program)};
|
||||
if (usage.rz_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Shader_SPIRV,
|
||||
"SPV_RZ {} start={:#010x} optimize={} support_float_controls={} separate_denorm_behavior={} separate_rounding_mode={} support_fp32_rounding_rtz={} broken_fp16_float_controls={} fp16_denorm={} fp32_denorm={} signed_nan16={} signed_nan32={} signed_nan64={} rz_inst_count={} mixed_fp32_rounding={}",
|
||||
StageName(program.stage), program.start_address, optimize,
|
||||
profile.support_float_controls, profile.support_separate_denorm_behavior,
|
||||
profile.support_separate_rounding_mode, profile.support_fp32_rounding_rtz,
|
||||
profile.has_broken_fp16_float_controls,
|
||||
DenormModeName(program.info.uses_fp16_denorms_flush,
|
||||
program.info.uses_fp16_denorms_preserve),
|
||||
DenormModeName(program.info.uses_fp32_denorms_flush,
|
||||
program.info.uses_fp32_denorms_preserve),
|
||||
profile.support_fp16_signed_zero_nan_preserve,
|
||||
profile.support_fp32_signed_zero_nan_preserve,
|
||||
profile.support_fp64_signed_zero_nan_preserve, usage.rz_count,
|
||||
usage.has_conflicting_rounding);
|
||||
}
|
||||
|
||||
void SetupRoundingControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
|
||||
Id main_func) {
|
||||
const Fp32RoundingUsage usage{CollectFp32RoundingUsage(program)};
|
||||
if (usage.rz_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (usage.has_conflicting_rounding) {
|
||||
if (Settings::values.renderer_debug) {
|
||||
LOG_INFO(Shader_SPIRV,
|
||||
"SPV_RZ {} start={:#010x} skipping_fp32_rtz_execution_mode reason=mixed_rounding",
|
||||
StageName(program.stage), program.start_address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!profile.support_fp32_rounding_rtz) {
|
||||
if (Settings::values.renderer_debug) {
|
||||
LOG_INFO(Shader_SPIRV,
|
||||
"SPV_RZ {} start={:#010x} skipping_fp32_rtz_execution_mode reason=unsupported_fp32_rtz",
|
||||
StageName(program.stage), program.start_address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.AddCapability(spv::Capability::RoundingModeRTZ);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::RoundingModeRTZ, 32U);
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
thread_local std::unique_ptr<spvtools::Optimizer> thread_optimizer;
|
||||
|
|
@ -647,14 +503,12 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
|
|||
|
||||
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings, bool optimize) {
|
||||
LogRzBackendSummary(profile, program, optimize);
|
||||
EmitContext ctx{profile, runtime_info, program, bindings};
|
||||
const Id main{DefineMain(ctx, program)};
|
||||
DefineEntryPoint(program, ctx, main);
|
||||
if (profile.support_float_controls) {
|
||||
ctx.AddExtension("SPV_KHR_float_controls");
|
||||
SetupDenormControl(profile, program, ctx, main);
|
||||
SetupRoundingControl(profile, program, ctx, main);
|
||||
SetupSignedNanCapabilities(profile, program, ctx, main);
|
||||
}
|
||||
SetupCapabilities(profile, program.info, ctx);
|
||||
|
|
@ -662,12 +516,6 @@ std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in
|
|||
PatchPhiNodes(program, ctx);
|
||||
|
||||
if (!optimize) {
|
||||
if (Settings::values.renderer_debug && ctx.log_rz_fp_controls) {
|
||||
const std::vector<u32> spirv{ctx.Assemble()};
|
||||
LOG_INFO(Shader_SPIRV, "SPV_RZ {} start={:#010x} assembled_words={} optimized_words={} validator_run=false",
|
||||
StageName(program.stage), program.start_address, spirv.size(), spirv.size());
|
||||
return spirv;
|
||||
}
|
||||
return ctx.Assemble();
|
||||
} else {
|
||||
std::vector<u32> spirv = ctx.Assemble();
|
||||
|
|
@ -687,11 +535,6 @@ std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in
|
|||
"Failed to optimize SPIRV shader output, continuing without optimization");
|
||||
result = std::move(spirv);
|
||||
}
|
||||
if (Settings::values.renderer_debug && ctx.log_rz_fp_controls) {
|
||||
LOG_INFO(Shader_SPIRV,
|
||||
"SPV_RZ {} start={:#010x} assembled_words={} optimized_words={} validator_run=false",
|
||||
StageName(program.stage), program.start_address, spirv.size(), result.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,57 +4,14 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
[[nodiscard]] constexpr std::string_view StageName(Stage stage) noexcept {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
return "VertexA";
|
||||
case Stage::VertexB:
|
||||
return "VertexB";
|
||||
case Stage::TessellationControl:
|
||||
return "TessellationControl";
|
||||
case Stage::TessellationEval:
|
||||
return "TessellationEval";
|
||||
case Stage::Geometry:
|
||||
return "Geometry";
|
||||
case Stage::Fragment:
|
||||
return "Fragment";
|
||||
case Stage::Compute:
|
||||
return "Compute";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view FmzName(IR::FmzMode fmz_mode) noexcept {
|
||||
switch (fmz_mode) {
|
||||
case IR::FmzMode::DontCare:
|
||||
return "DontCare";
|
||||
case IR::FmzMode::FTZ:
|
||||
return "FTZ";
|
||||
case IR::FmzMode::FMZ:
|
||||
return "FMZ";
|
||||
case IR::FmzMode::None:
|
||||
return "None";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
|
||||
const auto flags{inst->Flags<IR::FpControl>()};
|
||||
if (Settings::values.renderer_debug && ctx.log_rz_fp_controls &&
|
||||
flags.rounding == IR::FpRounding::RZ) {
|
||||
LOG_INFO(Shader_SPIRV,
|
||||
"SPV_RZ_EMIT {} start={:#010x} ir_opcode={} spirv_op=OpFMul no_contraction={} fmz={} float_controls_ext={}",
|
||||
StageName(ctx.stage), ctx.start_address, inst->GetOpcode(),
|
||||
flags.no_contraction, FmzName(flags.fmz_mode), ctx.profile.support_float_controls);
|
||||
}
|
||||
if (flags.no_contraction) {
|
||||
ctx.Decorate(op, spv::Decoration::NoContraction);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -474,44 +474,7 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
|
|||
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
|
||||
IR::Program& program, Bindings& bindings)
|
||||
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
|
||||
stage{program.stage}, start_address{program.start_address},
|
||||
log_rz_fp_controls{std::ranges::any_of(program.post_order_blocks, [](const IR::Block* block) {
|
||||
return std::ranges::any_of(block->Instructions(), [](const IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::FPAdd16:
|
||||
case IR::Opcode::FPFma16:
|
||||
case IR::Opcode::FPMul16:
|
||||
case IR::Opcode::FPRoundEven16:
|
||||
case IR::Opcode::FPFloor16:
|
||||
case IR::Opcode::FPCeil16:
|
||||
case IR::Opcode::FPTrunc16:
|
||||
case IR::Opcode::FPAdd32:
|
||||
case IR::Opcode::FPFma32:
|
||||
case IR::Opcode::FPMul32:
|
||||
case IR::Opcode::FPRoundEven32:
|
||||
case IR::Opcode::FPFloor32:
|
||||
case IR::Opcode::FPCeil32:
|
||||
case IR::Opcode::FPTrunc32:
|
||||
case IR::Opcode::FPOrdEqual32:
|
||||
case IR::Opcode::FPUnordEqual32:
|
||||
case IR::Opcode::FPOrdNotEqual32:
|
||||
case IR::Opcode::FPUnordNotEqual32:
|
||||
case IR::Opcode::FPOrdLessThan32:
|
||||
case IR::Opcode::FPUnordLessThan32:
|
||||
case IR::Opcode::FPOrdGreaterThan32:
|
||||
case IR::Opcode::FPUnordGreaterThan32:
|
||||
case IR::Opcode::FPOrdLessThanEqual32:
|
||||
case IR::Opcode::FPUnordLessThanEqual32:
|
||||
case IR::Opcode::FPOrdGreaterThanEqual32:
|
||||
case IR::Opcode::FPUnordGreaterThanEqual32:
|
||||
case IR::Opcode::ConvertF16F32:
|
||||
case IR::Opcode::ConvertF64F32:
|
||||
return inst.Flags<IR::FpControl>().rounding == IR::FpRounding::RZ;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
})}, texture_rescaling_index{bindings.texture_scaling_index},
|
||||
stage{program.stage}, texture_rescaling_index{bindings.texture_scaling_index},
|
||||
image_rescaling_index{bindings.image_scaling_index} {
|
||||
const bool is_unified{profile.unified_descriptor_binding};
|
||||
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
|
||||
|
|
|
|||
|
|
@ -216,8 +216,6 @@ public:
|
|||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
Stage stage{};
|
||||
u32 start_address{};
|
||||
bool log_rz_fp_controls{};
|
||||
|
||||
Id void_id{};
|
||||
Id U1{};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ struct Program {
|
|||
BlockList post_order_blocks;
|
||||
Info info;
|
||||
Stage stage{};
|
||||
u32 start_address{};
|
||||
std::array<u32, 3> workgroup_size{};
|
||||
OutputTopology output_topology{};
|
||||
u32 output_vertices{};
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
|
|
@ -25,214 +22,6 @@
|
|||
|
||||
namespace Shader::Maxwell {
|
||||
namespace {
|
||||
struct FpControlHistogram {
|
||||
std::array<u32, 2> total{};
|
||||
std::array<u32, 2> no_contraction{};
|
||||
std::array<std::array<u32, 5>, 2> rounding{};
|
||||
std::array<std::array<u32, 4>, 2> fmz{};
|
||||
std::array<std::array<std::array<u32, 4>, 5>, 2> combos{};
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr std::string_view StageName(Stage stage) noexcept {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
return "VertexA";
|
||||
case Stage::VertexB:
|
||||
return "VertexB";
|
||||
case Stage::TessellationControl:
|
||||
return "TessellationControl";
|
||||
case Stage::TessellationEval:
|
||||
return "TessellationEval";
|
||||
case Stage::Geometry:
|
||||
return "Geometry";
|
||||
case Stage::Fragment:
|
||||
return "Fragment";
|
||||
case Stage::Compute:
|
||||
return "Compute";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view RoundingName(IR::FpRounding rounding) noexcept {
|
||||
switch (rounding) {
|
||||
case IR::FpRounding::DontCare:
|
||||
return "DontCare";
|
||||
case IR::FpRounding::RN:
|
||||
return "RN";
|
||||
case IR::FpRounding::RM:
|
||||
return "RM";
|
||||
case IR::FpRounding::RP:
|
||||
return "RP";
|
||||
case IR::FpRounding::RZ:
|
||||
return "RZ";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view FmzName(IR::FmzMode fmz_mode) noexcept {
|
||||
switch (fmz_mode) {
|
||||
case IR::FmzMode::DontCare:
|
||||
return "DontCare";
|
||||
case IR::FmzMode::FTZ:
|
||||
return "FTZ";
|
||||
case IR::FmzMode::FMZ:
|
||||
return "FMZ";
|
||||
case IR::FmzMode::None:
|
||||
return "None";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::optional<size_t> FpControlBucket(const IR::Opcode opcode) noexcept {
|
||||
switch (opcode) {
|
||||
case IR::Opcode::FPAdd16:
|
||||
case IR::Opcode::FPFma16:
|
||||
case IR::Opcode::FPMul16:
|
||||
case IR::Opcode::FPRoundEven16:
|
||||
case IR::Opcode::FPFloor16:
|
||||
case IR::Opcode::FPCeil16:
|
||||
case IR::Opcode::FPTrunc16:
|
||||
return 0;
|
||||
case IR::Opcode::FPAdd32:
|
||||
case IR::Opcode::FPFma32:
|
||||
case IR::Opcode::FPMul32:
|
||||
case IR::Opcode::FPRoundEven32:
|
||||
case IR::Opcode::FPFloor32:
|
||||
case IR::Opcode::FPCeil32:
|
||||
case IR::Opcode::FPTrunc32:
|
||||
case IR::Opcode::FPOrdEqual32:
|
||||
case IR::Opcode::FPUnordEqual32:
|
||||
case IR::Opcode::FPOrdNotEqual32:
|
||||
case IR::Opcode::FPUnordNotEqual32:
|
||||
case IR::Opcode::FPOrdLessThan32:
|
||||
case IR::Opcode::FPUnordLessThan32:
|
||||
case IR::Opcode::FPOrdGreaterThan32:
|
||||
case IR::Opcode::FPUnordGreaterThan32:
|
||||
case IR::Opcode::FPOrdLessThanEqual32:
|
||||
case IR::Opcode::FPUnordLessThanEqual32:
|
||||
case IR::Opcode::FPOrdGreaterThanEqual32:
|
||||
case IR::Opcode::FPUnordGreaterThanEqual32:
|
||||
case IR::Opcode::ConvertF16F32:
|
||||
case IR::Opcode::ConvertF64F32:
|
||||
return 1;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
FpControlHistogram CollectFpControlHistogram(const IR::Program& program) {
|
||||
FpControlHistogram histogram{};
|
||||
for (const IR::Block* const block : program.post_order_blocks) {
|
||||
for (const IR::Inst& inst : block->Instructions()) {
|
||||
const std::optional<size_t> bucket{FpControlBucket(inst.GetOpcode())};
|
||||
if (!bucket) {
|
||||
continue;
|
||||
}
|
||||
const auto flags{inst.Flags<IR::FpControl>()};
|
||||
++histogram.total[*bucket];
|
||||
if (flags.no_contraction) {
|
||||
++histogram.no_contraction[*bucket];
|
||||
}
|
||||
++histogram.rounding[*bucket][static_cast<size_t>(flags.rounding)];
|
||||
++histogram.fmz[*bucket][static_cast<size_t>(flags.fmz_mode)];
|
||||
++histogram.combos[*bucket][static_cast<size_t>(flags.rounding)]
|
||||
[static_cast<size_t>(flags.fmz_mode)];
|
||||
}
|
||||
}
|
||||
return histogram;
|
||||
}
|
||||
|
||||
void LogRzFpControlTrace(Environment& env, const IR::Program& program) {
|
||||
std::array<u32, 2> totals{};
|
||||
for (const IR::Block* const block : program.post_order_blocks) {
|
||||
for (const IR::Inst& inst : block->Instructions()) {
|
||||
const std::optional<size_t> bucket{FpControlBucket(inst.GetOpcode())};
|
||||
if (!bucket) {
|
||||
continue;
|
||||
}
|
||||
const auto flags{inst.Flags<IR::FpControl>()};
|
||||
if (flags.rounding != IR::FpRounding::RZ) {
|
||||
continue;
|
||||
}
|
||||
++totals[*bucket];
|
||||
}
|
||||
}
|
||||
|
||||
if (totals[0] == 0 && totals[1] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr std::array<std::string_view, 2> precision_names{"fp16", "fp32"};
|
||||
LOG_INFO(Shader,
|
||||
"FP_RZ {} shader start={:#010x} blocks={} post_order_blocks={} fp16={} fp32={}",
|
||||
StageName(program.stage), env.StartAddress(), program.blocks.size(),
|
||||
program.post_order_blocks.size(), totals[0], totals[1]);
|
||||
|
||||
for (const IR::Block* const block : program.post_order_blocks) {
|
||||
u32 inst_index{};
|
||||
for (const IR::Inst& inst : block->Instructions()) {
|
||||
const std::optional<size_t> bucket{FpControlBucket(inst.GetOpcode())};
|
||||
if (!bucket) {
|
||||
++inst_index;
|
||||
continue;
|
||||
}
|
||||
const auto flags{inst.Flags<IR::FpControl>()};
|
||||
if (flags.rounding != IR::FpRounding::RZ) {
|
||||
++inst_index;
|
||||
continue;
|
||||
}
|
||||
LOG_INFO(Shader,
|
||||
"FP_RZ {} start={:#010x} block_order={} inst_index={} precision={} opcode={} no_contraction={} fmz={}",
|
||||
StageName(program.stage), env.StartAddress(), block->GetOrder(), inst_index,
|
||||
precision_names[*bucket], inst.GetOpcode(), flags.no_contraction,
|
||||
FmzName(flags.fmz_mode));
|
||||
++inst_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogFpControlHistogram(const IR::Program& program) {
|
||||
const FpControlHistogram histogram{CollectFpControlHistogram(program)};
|
||||
if (histogram.total[0] == 0 && histogram.total[1] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Shader, "FP_HIST {} shader blocks={} post_order_blocks={}",
|
||||
StageName(program.stage), program.blocks.size(), program.post_order_blocks.size());
|
||||
|
||||
constexpr std::array<std::string_view, 2> precision_names{"fp16", "fp32"};
|
||||
for (size_t bucket = 0; bucket < precision_names.size(); ++bucket) {
|
||||
if (histogram.total[bucket] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INFO(Shader,
|
||||
"FP_HIST {} total={} no_contraction={} rounding[DontCare={}, RN={}, RM={}, RP={}, RZ={}] fmz[DontCare={}, FTZ={}, FMZ={}, None={}]",
|
||||
precision_names[bucket], histogram.total[bucket], histogram.no_contraction[bucket],
|
||||
histogram.rounding[bucket][static_cast<size_t>(IR::FpRounding::DontCare)],
|
||||
histogram.rounding[bucket][static_cast<size_t>(IR::FpRounding::RN)],
|
||||
histogram.rounding[bucket][static_cast<size_t>(IR::FpRounding::RM)],
|
||||
histogram.rounding[bucket][static_cast<size_t>(IR::FpRounding::RP)],
|
||||
histogram.rounding[bucket][static_cast<size_t>(IR::FpRounding::RZ)],
|
||||
histogram.fmz[bucket][static_cast<size_t>(IR::FmzMode::DontCare)],
|
||||
histogram.fmz[bucket][static_cast<size_t>(IR::FmzMode::FTZ)],
|
||||
histogram.fmz[bucket][static_cast<size_t>(IR::FmzMode::FMZ)],
|
||||
histogram.fmz[bucket][static_cast<size_t>(IR::FmzMode::None)]);
|
||||
|
||||
for (size_t rounding = 0; rounding < histogram.combos[bucket].size(); ++rounding) {
|
||||
for (size_t fmz = 0; fmz < histogram.combos[bucket][rounding].size(); ++fmz) {
|
||||
const u32 count{histogram.combos[bucket][rounding][fmz]};
|
||||
if (count == 0) {
|
||||
continue;
|
||||
}
|
||||
LOG_INFO(Shader, "FP_HIST {} combo {} / {} = {}", precision_names[bucket],
|
||||
RoundingName(static_cast<IR::FpRounding>(rounding)),
|
||||
FmzName(static_cast<IR::FmzMode>(fmz)), count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) {
|
||||
size_t num_syntax_blocks{};
|
||||
for (const auto& node : syntax_list) {
|
||||
|
|
@ -458,7 +247,6 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
|||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
program.start_address = env.StartAddress();
|
||||
program.local_memory_size = env.LocalMemorySize();
|
||||
switch (program.stage) {
|
||||
case Stage::TessellationControl: {
|
||||
|
|
@ -527,11 +315,6 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
|||
Optimization::LayerPass(program, host_info);
|
||||
Optimization::VendorWorkaroundPass(program);
|
||||
|
||||
if (Settings::values.renderer_debug) {
|
||||
LogFpControlHistogram(program);
|
||||
LogRzFpControlTrace(env, program);
|
||||
}
|
||||
|
||||
CollectInterpolationInfo(env, program);
|
||||
AddNVNStorageBuffers(program);
|
||||
return program;
|
||||
|
|
@ -555,7 +338,6 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
|||
result.post_order_blocks.push_back(block);
|
||||
}
|
||||
result.stage = Stage::VertexB;
|
||||
result.start_address = env_vertex_b.StartAddress();
|
||||
result.info = vertex_a.info;
|
||||
result.local_memory_size = (std::max)(vertex_a.local_memory_size, vertex_b.local_memory_size);
|
||||
result.info.loads.mask |= vertex_b.info.loads.mask;
|
||||
|
|
@ -568,10 +350,6 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
|||
Optimization::VerificationPass(result);
|
||||
}
|
||||
Optimization::CollectShaderInfoPass(env_vertex_b, result);
|
||||
if (Settings::values.renderer_debug) {
|
||||
LogFpControlHistogram(result);
|
||||
LogRzFpControlTrace(env_vertex_b, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ struct Profile {
|
|||
bool support_float_controls{};
|
||||
bool support_separate_denorm_behavior{};
|
||||
bool support_separate_rounding_mode{};
|
||||
bool support_fp32_rounding_rtz{};
|
||||
bool support_fp16_denorm_preserve{};
|
||||
bool support_fp32_denorm_preserve{};
|
||||
bool support_fp16_denorm_flush{};
|
||||
|
|
|
|||
|
|
@ -194,7 +194,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||
.support_float_controls = false,
|
||||
.support_separate_denorm_behavior = false,
|
||||
.support_separate_rounding_mode = false,
|
||||
.support_fp32_rounding_rtz = false,
|
||||
.support_fp16_denorm_preserve = false,
|
||||
.support_fp32_denorm_preserve = false,
|
||||
.support_fp16_denorm_flush = false,
|
||||
|
|
|
|||
|
|
@ -602,6 +602,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
|||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_i
|
|||
|
||||
TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
|
||||
return *images.image_views[Rcas];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -140,6 +143,7 @@ void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
|
|||
cmdbuf.EndRenderPass();
|
||||
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
|
||||
*inout_image = *image.image;
|
||||
*inout_image_view = *image.image_view;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
|
@ -272,6 +272,7 @@ void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
|
|||
cmdbuf.EndRenderPass();
|
||||
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
|
||||
*inout_image = *images.images[Output];
|
||||
*inout_image_view = *images.image_views[Output];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -101,6 +104,7 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
|||
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() {
|
||||
|
|
|
|||
|
|
@ -377,7 +377,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||
float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
|
||||
.support_separate_rounding_mode =
|
||||
float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
|
||||
.support_fp32_rounding_rtz = float_control.shaderRoundingModeRTZFloat32 != VK_FALSE,
|
||||
.support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE,
|
||||
.support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
|
||||
.support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
|
||||
|
|
@ -752,6 +751,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
|||
descriptor_pool, guest_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
|
||||
std::move(modules), infos);
|
||||
|
||||
} catch (const vk::Exception& exception) {
|
||||
const auto hash = key.Hash();
|
||||
LOG_ERROR(
|
||||
Render_Vulkan,
|
||||
"Failed to create graphics pipeline 0x{:016x}: {} (result={}, eds={}, eds2={}, "
|
||||
"eds2_logic_op={}, topology={}, provoking_last={}, xfb={}, conservative={})",
|
||||
hash, exception.what(), static_cast<int>(exception.GetResult()),
|
||||
key.state.extended_dynamic_state != 0, key.state.extended_dynamic_state_2 != 0,
|
||||
key.state.extended_dynamic_state_2_logic_op != 0, static_cast<u32>(key.state.topology.Value()),
|
||||
key.state.provoking_vertex_last != 0, key.state.xfb_enabled != 0,
|
||||
key.state.conservative_raster_enable != 0);
|
||||
return nullptr;
|
||||
|
||||
} catch (const Shader::Exception& exception) {
|
||||
auto hash = key.Hash();
|
||||
size_t env_index{0};
|
||||
|
|
|
|||
|
|
@ -168,6 +168,11 @@ bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (pipeline->UsesExtendedDynamicState() || pipeline->UsesExtendedDynamicState2() ||
|
||||
pipeline->UsesExtendedDynamicState2LogicOp()) {
|
||||
state_tracker.InvalidateExtendedDynamicStates();
|
||||
}
|
||||
|
||||
if (!pipeline->UsesExtendedDynamicState()) {
|
||||
state.needs_state_enable_refresh = true;
|
||||
} else if (state.needs_state_enable_refresh) {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,21 @@ public:
|
|||
(*flags)[Dirty::StateEnable] = true;
|
||||
}
|
||||
|
||||
void InvalidateExtendedDynamicStates() {
|
||||
(*flags)[Dirty::Viewports] = true;
|
||||
(*flags)[Dirty::Scissors] = true;
|
||||
(*flags)[Dirty::CullMode] = true;
|
||||
(*flags)[Dirty::DepthCompareOp] = true;
|
||||
(*flags)[Dirty::FrontFace] = true;
|
||||
(*flags)[Dirty::StencilOp] = true;
|
||||
(*flags)[Dirty::StateEnable] = true;
|
||||
(*flags)[Dirty::PrimitiveRestartEnable] = true;
|
||||
(*flags)[Dirty::RasterizerDiscardEnable] = true;
|
||||
(*flags)[Dirty::DepthBiasEnable] = true;
|
||||
(*flags)[Dirty::LogicOp] = true;
|
||||
current_topology = INVALID_TOPOLOGY;
|
||||
}
|
||||
|
||||
bool TouchViewports() {
|
||||
const bool dirty_viewports = Exchange(Dirty::Viewports, false);
|
||||
const bool rescale_viewports = Exchange(VideoCommon::Dirty::RescaleViewports, false);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,9 @@ bool Swapchain::AcquireNextImage() {
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
scheduler.Wait(resource_ticks[image_index]);
|
||||
#else
|
||||
switch (Settings::values.frame_pacing_mode.GetValue()) {
|
||||
case Settings::FramePacingMode::Target_Auto:
|
||||
scheduler.Wait(resource_ticks[image_index]);
|
||||
|
|
@ -187,6 +190,7 @@ bool Swapchain::AcquireNextImage() {
|
|||
scheduler.Wait(resource_ticks[image_index], 120.0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
resource_ticks[image_index] = scheduler.CurrentTick();
|
||||
|
||||
|
|
|
|||
|
|
@ -502,6 +502,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||
|
||||
if (is_qualcomm) {
|
||||
must_emulate_scaled_formats = true;
|
||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
|
||||
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
|
||||
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue