Compare commits

..

1 Commits

Author SHA1 Message Date
MaranBr c31ce3474e Remove Ignore Global Monitor from CPU Accuracy Auto 2026-04-19 00:56:59 +02:00
73 changed files with 6617 additions and 6574 deletions
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+230 -215
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+236 -221
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+301 -287
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+233 -218
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+234 -220
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
+232 -217
View File
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.drawable.Icon
@@ -101,7 +100,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
private var romSwapGeneration = 0
private var hasEmulationSession = processHasEmulationSession
private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() }
private val pictureInPictureFailureActions: MutableSet<String> = mutableSetOf()
private fun onRomSwapStopTimeout() {
if (!isWaitingForRomSwapStop) {
@@ -268,18 +266,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ||
!isPictureInPictureSupported() ||
!BooleanSetting.PICTURE_IN_PICTURE.getBoolean() ||
isInPictureInPictureMode
) {
return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
runPictureInPictureAction("enter picture-in-picture mode") {
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
}
}
@@ -659,29 +651,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
return this.apply { setActions(pictureInPictureActions) }
}
private fun isPictureInPictureSupported() =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
private fun runPictureInPictureAction(actionName: String, action: () -> Unit) {
try {
action()
} catch (e: IllegalStateException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
} catch (e: UnsupportedOperationException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
}
}
fun buildPictureInPictureParams() {
if (!isPictureInPictureSupported()) {
return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -691,9 +661,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive
)
}
runPictureInPictureAction("set picture-in-picture params") {
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
fun displayMultiplayerDialog() {
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -37,10 +33,8 @@ object SoftwareKeyboard {
val emulationActivity = NativeLibrary.sEmulationActivity.get()
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
overlayView.requestFocus()
val im =
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
im.restartInput(overlayView)
im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
@@ -17,7 +17,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"),
CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"),
USE_DOCKED_MODE("use_docked_mode"),
USE_AUTO_STUB("use_auto_stub"),
@@ -756,13 +756,6 @@ abstract class SettingsItem(
descriptionId = R.string.fix_bloom_effects_description
)
)
put(
SwitchSetting(
BooleanSetting.EMULATE_BGR565,
titleId = R.string.emulate_bgr565,
descriptionId = R.string.emulate_bgr565_description
)
)
put(
SwitchSetting(
BooleanSetting.CPUOPT_UNSAFE_HOST_MMU,
@@ -293,7 +293,6 @@ class SettingsFragmentPresenter(
add(IntSetting.FAST_GPU_TIME.key)
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
@@ -17,23 +17,16 @@ import android.graphics.drawable.VectorDrawable
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.InputType
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.WindowInsets
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.core.content.ContextCompat
import androidx.window.layout.WindowMetricsCalculator
import kotlin.math.max
import kotlin.math.min
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
@@ -56,7 +49,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
private val imeEditable = Editable.Factory.getInstance().newEditable("")
private var inEditMode = false
private var gamelessMode = false
@@ -83,63 +75,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// External listener for EmulationFragment joypad overlay auto-hide
var touchEventListener: ((MotionEvent) -> Unit)? = null
override fun onCheckIsTextEditor(): Boolean = true
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
imeEditable.clear()
outAttrs.inputType =
InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI or EditorInfo.IME_ACTION_DONE
outAttrs.initialSelStart = 0
outAttrs.initialSelEnd = 0
return object : BaseInputConnection(this, true) {
override fun getEditable(): Editable = imeEditable
override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
if (!text.isNullOrEmpty()) {
forwardCommittedText(text)
}
return super.commitText(text, newCursorPosition)
}
override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean {
repeat(beforeLength.coerceAtLeast(0)) {
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_DEL)
}
return super.deleteSurroundingText(beforeLength, afterLength)
}
override fun sendKeyEvent(event: KeyEvent): Boolean {
if (event.action != KeyEvent.ACTION_DOWN) {
return true
}
when (event.keyCode) {
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_DEL,
KeyEvent.KEYCODE_ENTER -> {
NativeLibrary.submitInlineKeyboardInput(event.keyCode)
}
else -> {
val textChar = event.unicodeChar
if (textChar != 0) {
NativeLibrary.submitInlineKeyboardText(textChar.toChar().toString())
}
}
}
return true
}
override fun performEditorAction(actionCode: Int): Boolean {
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
return true
}
}
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
@@ -184,25 +119,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
}
private fun forwardCommittedText(text: CharSequence) {
val builder = StringBuilder()
text.forEach { character ->
when (character) {
'\n' -> {
if (builder.isNotEmpty()) {
NativeLibrary.submitInlineKeyboardText(builder.toString())
builder.clear()
}
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
}
else -> builder.append(character)
}
}
if (builder.isNotEmpty()) {
NativeLibrary.submitInlineKeyboardText(builder.toString())
}
}
private fun drawGrid(canvas: Canvas) {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
val width = canvas.width
@@ -476,25 +476,21 @@
<string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。</string>
<string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string>
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
<string name="renderer_asynchronous_gpu_emulation_description">异步运行 GPU 模拟,以减少 CPU 停顿并提高吞吐量。仅当遇到与时序相关的问题时才应禁用此功能。</string>
<string name="renderer_async_presentation">异步呈现</string>
<string name="renderer_async_presentation_description">通过将呈现操作移至单独的 CPU 线程来略微提升性能。</string>
<string name="renderer_reactive_flushing">启用反应性刷新</string>
<string name="renderer_reactive_flushing_description">通过牺牲性能来提高某些游戏的渲染精度。</string>
<string name="renderer_reactive_flushing_description">牺牲性能,提高某些游戏的渲染精度。</string>
<string name="enable_buffer_history">启用缓冲区历史</string>
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
<string name="enable_buffer_history_description">允许访问之前的缓冲状态。\n这个选项可能会提升某些游戏的渲染质量和性能一致性。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 驱动程序。在旧版驱动程序上会导致程序崩溃。</string>
<string name="use_optimized_vertex_buffers_description">实现优化顶点缓冲区绑定以提升性能。需要 Mesa 26.0+ Turnip 驱动。老驱动会崩溃。</string>
<string name="hacks">Hacks</string>
<string name="fast_gpu_time">GPU 超频频率</string>
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。设置为 256 可获得最佳性能,设置为 512 可获得最佳画面保真度。</string>
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。使用 256 以获得最大性能,使用 512 以获得最大图形保真度。</string>
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
<string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(Adreno 700)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现画面显示问题。</string>
<string name="fix_bloom_effects">修复泛光效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(LA/EOW) 在 Adreno 700 系列 GPU 上的模糊感,并移除《横冲直撞》(Burnout) 中的模糊特效。警告:可能会导致其他游戏出现画面花屏或异常。</string>
<string name="renderer_asynchronous_shaders">使用异步着色器</string>
<string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。</string>
<string name="gpu_unswizzle_settings">GPU 还原设置</string>
@@ -501,7 +501,7 @@
<string name="enable_buffer_history">Enable buffer history</string>
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers.</string>
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers. Will crash on older drivers.</string>
<string name="hacks">Hacks</string>
@@ -510,9 +510,7 @@
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string>
<string name="emulate_bgr565_description">Fixes problems with inverted colors in games or strange artifacts or strange shadows.</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
@@ -243,9 +243,6 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
static_cast<s32>(m_current_text.size()));
break;
case KEYCODE_DEL:
if (m_current_text.empty()) {
return;
}
m_current_text.pop_back();
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
static_cast<int>(m_current_text.size()));
+1 -4
View File
@@ -555,9 +555,6 @@ struct Values {
SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects",
Category::RendererHacks};
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks};
@@ -587,7 +584,7 @@ struct Values {
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
#if defined(ANDROID)
ExtendedDynamicState::Disabled,
ExtendedDynamicState::EDS1,
#elif defined(__APPLE__)
ExtendedDynamicState::Disabled,
#else
@@ -23,14 +23,16 @@ static const auto default_cg_mode = nullptr; //Allow RWE
namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
if (waitpkg) {
code.push(Xbyak::util::eax);
code.push(Xbyak::util::ebx);
code.push(Xbyak::util::edx);
}
Xbyak::Label start, loop;
code.jmp(start, code.T_NEAR);
code.L(loop);
if (waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
code.push(Xbyak::util::rax);
code.push(Xbyak::util::rbx);
code.push(Xbyak::util::rdx);
// TODO: This clobbers EAX and EDX did we tell the regalloc?
// ARM ptr for address-monitoring
code.umonitor(ptr);
@@ -47,9 +49,6 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
code.umwait(Xbyak::util::ebx);
// CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write
// CF == 0 if we exited the wait for any other reason
code.pop(Xbyak::util::rdx);
code.pop(Xbyak::util::rbx);
code.pop(Xbyak::util::rax);
} else {
code.pause();
}
@@ -58,6 +57,11 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
/*code.lock();*/ code.xchg(code.dword[ptr], tmp);
code.test(tmp, tmp);
code.jnz(loop, code.T_NEAR);
if (waitpkg) {
code.pop(Xbyak::util::edx);
code.pop(Xbyak::util::ebx);
code.pop(Xbyak::util::eax);
}
}
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) {
@@ -178,9 +178,6 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer);
ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride);
ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset);
if (ctx.stage == Stage::Geometry && xfb_varying->stream != 0) {
ctx.Decorate(id, spv::Decoration::Stream, xfb_varying->stream);
}
}
if (num_components < 4 || element > 0) {
const std::string_view subswizzle{swizzle.substr(element, num_components)};
-1
View File
@@ -76,7 +76,6 @@ enum class TessSpacing {
struct TransformFeedbackVarying {
u32 buffer{};
u32 stream{};
u32 stride{};
u32 offset{};
u32 components{};
+15 -18
View File
@@ -1067,29 +1067,26 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
HostBindings<typename P::Buffer> host_bindings;
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = channel_state->transform_feedback_buffers[index];
const auto& control = maxwell3d->regs.transform_feedback.controls[index];
const bool has_layout = control.varying_count != 0 || control.stride != 0;
Buffer* host_buffer = &slot_buffers[NULL_BUFFER_ID];
u32 offset = 0;
u32 size = 0;
if (has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) {
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
size = binding.size;
SynchronizeBuffer(buffer, binding.device_addr, size);
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size);
host_buffer = &buffer;
if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
maxwell3d->regs.transform_feedback.controls[index].stride == 0) {
break;
}
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.device_addr, size);
host_bindings.buffers.push_back(host_buffer);
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(size);
}
runtime.BindTransformFeedbackBuffers(host_bindings);
if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings);
}
}
template <class P>
+2 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -92,16 +92,7 @@ void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
}
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) {
if (Settings::IsGPULevelHigh()) {
if (!is_async) {
PushCommand(FlushRegionCommand(addr, size));
} else {
auto& gpu = system.GPU();
const u64 fence = gpu.RequestFlush(addr, size);
TickGPU();
gpu.WaitForSyncOperation(fence);
}
}
// Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
rasterizer->OnCacheInvalidation(addr, size);
}
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -8,22 +5,16 @@
layout(local_size_x = 1) in;
layout(std430, binding = 0) readonly buffer Query {
uint data[];
layout(std430, binding = 0) buffer Query {
uvec2 initial;
uvec2 unknown;
uvec2 current;
};
layout(std430, binding = 1) writeonly buffer Result {
layout(std430, binding = 1) buffer Result {
uint result;
};
layout(push_constant) uniform PushConstants {
uint compare_to_zero;
};
void main() {
if (compare_to_zero != 0u) {
result = (data[0] != 0u && data[1] != 0u) ? 1u : 0u;
} else {
result = (data[0] == data[4] && data[1] == data[5]) ? 1u : 0u;
}
result = all(equal(initial, current)) ? 1 : 0;
}
+2 -2
View File
@@ -285,11 +285,11 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
}
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
if (!force) {
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
Fallback(maxwell3d, parameters);
return;
}
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = true;
params.is_indexed = false;
-1
View File
@@ -412,7 +412,6 @@ bool QueryCacheBase<Traits>::AccelerateHostConditionalRendering() {
.found_query = nullptr,
};
}
it_current = it_current_2;
}
auto* query = impl->ObtainQuery(it_current->second);
qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) &&
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -78,7 +75,7 @@ public:
}
u64 GetDependentMask() const {
return dependent_mask;
return dependence_mask;
}
u64 GetAmendValue() const {
@@ -629,9 +629,6 @@ void RasterizerOpenGL::ReleaseFences(bool force) {
void RasterizerOpenGL::FlushAndInvalidateRegion(DAddr addr, u64 size,
VideoCommon::CacheType which) {
if (Settings::IsGPULevelHigh()) {
FlushRegion(addr, size, which);
}
InvalidateRegion(addr, size, which);
}
@@ -190,7 +190,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
}
}
}
dynamic_state.Refresh3(regs, features);
if (!extended_dynamic_state_3_enables) {
dynamic_state.Refresh3(regs);
}
if (xfb_enabled) {
RefreshXfbState(xfb_state, regs);
}
@@ -293,22 +295,16 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
}
void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs,
const DynamicFeatures& features) {
if (!features.has_dynamic_state3_logic_op_enable) {
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
}
if (!features.has_dynamic_state3_depth_clamp_enable) {
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
}
if (!features.has_dynamic_state3_line_stipple_enable) {
line_stipple_enable.Assign(regs.line_stipple_enable);
}
void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) {
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
line_stipple_enable.Assign(regs.line_stipple_enable);
}
size_t FixedPipelineState::Hash() const noexcept {
@@ -27,9 +27,6 @@ struct DynamicFeatures {
bool has_extended_dynamic_state_2_patch_control_points;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_state3_depth_clamp_enable;
bool has_dynamic_state3_logic_op_enable;
bool has_dynamic_state3_line_stipple_enable;
bool has_dynamic_vertex_input;
bool has_provoking_vertex;
bool has_provoking_vertex_first_mode;
@@ -178,7 +175,7 @@ struct FixedPipelineState {
void Refresh(const Maxwell& regs);
void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
bool base_features_supported);
void Refresh3(const Maxwell& regs, const DynamicFeatures& features);
void Refresh3(const Maxwell& regs);
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func);
@@ -268,7 +265,8 @@ struct FixedPipelineState {
return sizeof(*this);
}
if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
return offsetof(FixedPipelineState, attachments);
// Exclude dynamic state and attributes
return offsetof(FixedPipelineState, dynamic_state);
}
if (dynamic_vertex_input) {
// Exclude dynamic state
@@ -8,7 +8,6 @@
#include <array>
#include <cstring>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
@@ -172,11 +171,7 @@ try
RendererVulkan::~RendererVulkan() {
scheduler.RegisterOnSubmit([] {});
scheduler.Finish();
{
std::scoped_lock lock{scheduler.submit_mutex};
void(device.GetLogical().WaitIdle());
}
void(device.GetLogical().WaitIdle());
}
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
@@ -8,7 +8,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <vulkan/vulkan_core.h>
#include <mutex>
#include "video_core/framebuffer_config.h"
#include "video_core/present.h"
#include "video_core/renderer_vulkan/present/filters.h"
@@ -32,10 +31,7 @@ BlitScreen::~BlitScreen() = default;
void BlitScreen::WaitIdle() {
present_manager.WaitPresent();
scheduler.Finish();
{
std::scoped_lock lock{scheduler.submit_mutex};
device.GetLogical().WaitIdle();
}
device.GetLogical().WaitIdle();
}
void BlitScreen::SetWindowAdaptPass() {
@@ -637,10 +637,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
auto handle = bindings.buffers[i]->Handle();
if (handle == VK_NULL_HANDLE) {
ReserveNullBuffer();
handle = *null_buffer;
bindings.offsets[i] = 0;
bindings.sizes[i] = 0;
bindings.sizes[i] = VK_WHOLE_SIZE;
if (!device.HasNullDescriptor()) {
ReserveNullBuffer();
handle = *null_buffer;
}
}
buffer_handles[i] = handle;
}
@@ -228,10 +228,6 @@ struct QueriesPrefixScanPushConstants {
u32 accumulation_limit;
u32 buffer_offset;
};
struct ConditionalRenderingResolvePushConstants {
u32 compare_to_zero;
};
} // Anonymous namespace
ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
@@ -417,8 +413,7 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS,
INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(ConditionalRenderingResolvePushConstants)>,
INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, nullptr,
RESOLVE_CONDITIONAL_RENDER_COMP_SPV),
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
@@ -435,7 +430,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) {
scheduler.Record([this, descriptor_data](vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
@@ -448,9 +443,6 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT,
};
const ConditionalRenderingResolvePushConstants uniforms{
.compare_to_zero = compare_to_zero ? 1U : 0U,
};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
@@ -458,11 +450,9 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(1, 1, 1);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0,
write_barrier);
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier);
});
}
@@ -530,7 +520,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
@@ -467,10 +467,6 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
bind_stage_info(4);
}
if (regs.transform_feedback_enabled != 0) {
scheduler.RequestOutsideRenderPassOperationContext();
}
buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed);
@@ -485,12 +485,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
device.IsExtExtendedDynamicState3BlendingSupported();
dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported();
dynamic_features.has_dynamic_state3_depth_clamp_enable =
device.SupportsDynamicState3DepthClampEnable();
dynamic_features.has_dynamic_state3_logic_op_enable =
device.SupportsDynamicState3LogicOpEnable();
dynamic_features.has_dynamic_state3_line_stipple_enable =
device.SupportsDynamicState3LineStippleEnable();
// VIDS: Independent toggle (not affected by dyna_state levels)
dynamic_features.has_dynamic_vertex_input =
@@ -189,7 +189,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
frame->image = memory_allocator.CreateImage({
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT,
.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
.imageType = VK_IMAGE_TYPE_2D,
.format = swapchain.GetImageFormat(),
.extent =
+26 -129
View File
@@ -157,9 +157,8 @@ public:
ReserveHostQuery();
scheduler.Record([query_pool = current_query_pool,
query_index = current_bank_slot](vk::CommandBuffer cmdbuf) {
query_index = current_bank_slot](vk::CommandBuffer cmdbuf) {
const bool use_precise = Settings::IsGPULevelHigh();
cmdbuf.ResetQueryPool(query_pool, static_cast<u32>(query_index), 1);
cmdbuf.BeginQuery(query_pool, static_cast<u32>(query_index),
use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
});
@@ -221,7 +220,8 @@ public:
}
PauseCounter();
const auto driver_id = device.GetDriverID();
if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
pending_sync.clear();
sync_values_stash.clear();
return;
@@ -666,18 +666,13 @@ public:
offsets.fill(0);
last_queries.fill(0);
last_queries_stride.fill(1);
stream_to_slot.fill(INVALID_SLOT);
VkBufferUsageFlags counter_buffer_usage =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (device.IsExtTransformFeedbackSupported()) {
counter_buffer_usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
}
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS,
.usage = counter_buffer_usage,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -697,9 +692,6 @@ public:
~TFBCounterStreamer() = default;
void StartCounter() override {
if (!device.IsExtTransformFeedbackSupported()) {
return;
}
FlushBeginTFB();
has_started = true;
}
@@ -714,9 +706,7 @@ public:
void CloseCounter() override {
if (has_flushed_end_pending) {
if (scheduler.IsRenderPassActive()) {
FlushEndTFB();
}
FlushEndTFB();
}
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
if (maxwell3d.regs.transform_feedback_enabled == 0) {
@@ -766,33 +756,18 @@ public:
if (has_timestamp) {
new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp;
}
if (!device.IsExtTransformFeedbackSupported()) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
if (!subreport_) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
const size_t subreport = static_cast<size_t>(*subreport_);
if (subreport >= NUM_STREAMS) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
last_queries[subreport] = address;
if ((streams_mask & (1ULL << subreport)) == 0) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
const size_t slot = stream_to_slot[subreport];
if (slot >= NUM_STREAMS) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
scheduler.RequestOutsideRenderPassOperationContext();
CloseCounter();
auto [bank_slot, data_slot] = ProduceCounterBuffer(slot);
auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport);
new_query->start_bank_id = static_cast<u32>(bank_slot);
new_query->size_banks = 1;
new_query->start_slot = static_cast<u32>(data_slot);
@@ -803,9 +778,6 @@ public:
}
std::optional<std::pair<DAddr, size_t>> GetLastQueryStream(size_t stream) {
if (stream >= NUM_STREAMS) {
return std::nullopt;
}
if (last_queries[stream] != 0) {
std::pair<DAddr, size_t> result(last_queries[stream], last_queries_stride[stream]);
return result;
@@ -817,10 +789,6 @@ public:
return out_topology;
}
u32 GetPatchVertices() const {
return patch_vertices;
}
bool HasUnsyncedQueries() const override {
return !pending_flush_queries.empty();
}
@@ -887,9 +855,6 @@ public:
private:
void FlushBeginTFB() {
if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
return;
}
if (has_flushed_end_pending) [[unlikely]] {
return;
}
@@ -903,24 +868,12 @@ private:
});
return;
}
static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
};
scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0,
COUNTER_RESUME_BARRIER);
cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data());
});
}
void FlushEndTFB() {
if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
return;
}
if (!has_flushed_end_pending) [[unlikely]] {
UNREACHABLE();
return;
@@ -946,48 +899,28 @@ private:
void UpdateBuffers() {
last_queries.fill(0);
last_queries_stride.fill(1);
stream_to_slot.fill(INVALID_SLOT);
streams_mask = 0; // reset previously recorded streams
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0;
out_topology = maxwell3d.draw_manager->GetDrawState().topology;
patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) {
switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) {
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points:
out_topology = Maxwell3D::Regs::PrimitiveTopology::Points;
break;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines:
out_topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
break;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW:
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW:
out_topology = Maxwell3D::Regs::PrimitiveTopology::TriangleStrip;
break;
}
}
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
continue;
}
buffers_count = std::max<size_t>(buffers_count, i + 1);
const size_t stream = tf.controls[i].stream;
if (stream >= last_queries_stride.size()) {
LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream);
continue;
}
if ((streams_mask & (1ULL << stream)) != 0) {
continue;
}
last_queries_stride[stream] = tf.controls[i].stride;
stream_to_slot[stream] = i;
streams_mask |= 1ULL << stream;
buffers_count = std::max<size_t>(buffers_count, stream + 1);
}
});
}
std::pair<size_t, size_t> ProduceCounterBuffer(size_t slot_index) {
std::pair<size_t, size_t> ProduceCounterBuffer(size_t stream) {
if (current_bank == nullptr || current_bank->IsClosed()) {
current_bank_id =
bank_pool.ReserveBank([this](std::deque<TFBQueryBank>& queue, size_t index) {
@@ -1013,8 +946,7 @@ private:
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_buffer = current_bank->GetBuffer(),
src_buffer = counter_buffers[slot_index],
src_offset = offsets[slot_index],
src_buffer = counter_buffers[stream], src_offset = offsets[stream],
slot](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER);
@@ -1033,7 +965,6 @@ private:
friend class PrimitivesSucceededStreamer;
static constexpr size_t NUM_STREAMS = 4;
static constexpr size_t INVALID_SLOT = NUM_STREAMS;
QueryCacheRuntime& runtime;
const Device& device;
@@ -1063,9 +994,7 @@ private:
std::array<VkDeviceSize, NUM_STREAMS> offsets{};
std::array<DAddr, NUM_STREAMS> last_queries;
std::array<size_t, NUM_STREAMS> last_queries_stride;
std::array<size_t, NUM_STREAMS> stream_to_slot;
Maxwell3D::Regs::PrimitiveTopology out_topology;
u32 patch_vertices{1};
u64 streams_mask;
};
@@ -1086,7 +1015,6 @@ public:
u64 stride{};
DAddr dependant_address{};
Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points};
u32 patch_vertices{1};
size_t dependant_index{};
bool dependant_manage{};
};
@@ -1103,10 +1031,6 @@ public:
~PrimitivesSucceededStreamer() = default;
void ResetCounter() override {
tfb_streamer.ResetCounter();
}
size_t WriteCounter(DAddr address, bool has_timestamp, u32 value,
std::optional<u32> subreport_) override {
auto index = BuildQuery();
@@ -1124,7 +1048,6 @@ public:
auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport);
bool must_manage_dependance = false;
new_query->topology = tfb_streamer.GetOutputTopology();
new_query->patch_vertices = tfb_streamer.GetPatchVertices();
if (dependant_address_opt) {
auto [dep_address, stride] = *dependant_address_opt;
new_query->dependant_address = dep_address;
@@ -1145,7 +1068,6 @@ public:
}
new_query->stride = 1;
runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) {
new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
@@ -1209,39 +1131,27 @@ public:
}
}
query->value = [&]() -> u64 {
const auto saturating_subtract = [](u64 value, u64 amount) {
return value > amount ? value - amount : 0;
};
switch (query->topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points:
return num_vertices;
case Maxwell3D::Regs::PrimitiveTopology::Lines:
return num_vertices / 2;
case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
return num_vertices > 1 ? num_vertices : 0;
return (num_vertices / 2) + 1;
case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
return saturating_subtract(num_vertices, 1);
case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
return saturating_subtract(num_vertices, 3);
return num_vertices - 1;
case Maxwell3D::Regs::PrimitiveTopology::Patches:
case Maxwell3D::Regs::PrimitiveTopology::Triangles:
return num_vertices / 3;
case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 6;
return num_vertices / 3;
case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
return saturating_subtract(num_vertices, 2);
case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
return num_vertices > 4 ? (num_vertices - 4) / 2 : 0;
return num_vertices - 2;
case Maxwell3D::Regs::PrimitiveTopology::Quads:
return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
return num_vertices > 2 ? (num_vertices - 2) / 2 : 0;
case Maxwell3D::Regs::PrimitiveTopology::Polygon:
return num_vertices >= 3 ? 1U : 0U;
case Maxwell3D::Regs::PrimitiveTopology::Patches:
return num_vertices / std::max<u64>(query->patch_vertices, 1U);
return 1U;
default:
return num_vertices;
}
@@ -1292,24 +1202,16 @@ struct QueryCacheRuntimeImpl {
hcr_setup.pNext = nullptr;
hcr_setup.flags = 0;
const bool has_conditional_rendering = device.IsExtConditionalRendering();
if (has_conditional_rendering) {
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
}
VkBufferUsageFlags hcr_buffer_usage =
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (has_conditional_rendering) {
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
}
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = sizeof(u32),
.usage = hcr_buffer_usage,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -1436,17 +1338,15 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
}
}
void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
bool compare_to_zero) {
void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) {
VkBuffer to_resolve;
u32 to_resolve_offset;
const u32 resolve_size = compare_to_zero ? 8 : 24;
{
std::scoped_lock lk(impl->buffer_cache.mutex);
const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::NoSynchronize;
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing;
const auto [buffer, offset] =
impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op);
impl->buffer_cache.ObtainCPUBuffer(address, 24, sync_info, post_op);
to_resolve = buffer->Handle();
to_resolve_offset = static_cast<u32>(offset);
}
@@ -1455,7 +1355,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
PauseHostConditionalRendering();
}
impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve,
to_resolve_offset, compare_to_zero);
to_resolve_offset, false);
impl->hcr_setup.buffer = *impl->hcr_resolve_buffer;
impl->hcr_setup.offset = 0;
impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
@@ -1471,7 +1371,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup
if (!impl->device.IsExtConditionalRendering()) {
return false;
}
HostConditionalRenderingCompareBCImpl(object_1.address, true, true);
HostConditionalRenderingCompareValueImpl(object_1, false);
return true;
}
@@ -1520,8 +1420,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh();
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
EndHostConditionalRendering();
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
return true;
}
@@ -1538,12 +1437,10 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
}
if (!is_gpu_high) {
EndHostConditionalRendering();
return true;
}
if (!is_in_bc[0] && !is_in_bc[1]) {
EndHostConditionalRendering();
return true;
}
HostConditionalRenderingCompareBCImpl(object_1.address, equal_check);
@@ -63,8 +63,7 @@ public:
private:
void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal);
void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
bool compare_to_zero = false);
void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal);
friend struct QueryCacheRuntimeImpl;
std::unique_ptr<QueryCacheRuntimeImpl> impl;
};
+35 -102
View File
@@ -173,28 +173,6 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
}
return params;
}
bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
switch (topology) {
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
return false;
default:
return true;
}
}
bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) {
return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(topology) ||
(topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsPatchListPrimitiveRestartSupported()));
}
} // Anonymous namespace
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -247,7 +225,6 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates();
query_cache.NotifySegment(true);
HandleTransformFeedback();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
@@ -359,7 +336,6 @@ void RasterizerVulkan::DrawTexture() {
UpdateDynamicStates();
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
@@ -599,17 +575,11 @@ void RasterizerVulkan::DispatchCompute() {
}
void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) {
switch (type) {
case VideoCommon::QueryType::ZPassPixelCount64:
case VideoCommon::QueryType::StreamingByteCount:
case VideoCommon::QueryType::StreamingPrimitivesSucceeded:
case VideoCommon::QueryType::VtgPrimitivesOut:
query_cache.CounterReset(type);
return;
default:
if (type != VideoCommon::QueryType::ZPassPixelCount64) {
LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type);
return;
}
query_cache.CounterReset(type);
}
void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
@@ -796,9 +766,6 @@ void RasterizerVulkan::ReleaseFences(bool force) {
void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size,
VideoCommon::CacheType which) {
if (Settings::IsGPULevelHigh()) {
FlushRegion(addr, size, which);
}
InvalidateRegion(addr, size, which);
}
@@ -863,10 +830,6 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
return query_cache.AccelerateHostConditionalRendering();
}
bool RasterizerVulkan::HasDrawTransformFeedback() {
return device.IsTransformFeedbackDrawSupported();
}
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -1013,12 +976,6 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
void RasterizerVulkan::UpdateDynamicStates() {
auto& regs = maxwell3d->regs;
auto& flags = maxwell3d->dirty.flags;
const auto topology = maxwell3d->draw_manager->GetDrawState().topology;
if (state_tracker.ChangePrimitiveTopology(topology)) {
flags[Dirty::DepthBiasEnable] = true;
flags[Dirty::PrimitiveRestartEnable] = true;
}
// Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting
UpdateViewportsState(regs);
@@ -1127,9 +1084,6 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
if (!state_tracker.TouchViewports()) {
return;
}
maxwell3d->dirty.flags[Dirty::Scissors] = true;
if (!regs.viewport_scale_offset_enabled) {
float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y);
@@ -1147,12 +1101,8 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) {
const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
std::array<VkViewport, Maxwell::NumViewports> viewport_list{};
viewport_list.fill(viewport);
const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
cmdbuf.SetViewport(0, viewports);
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) {
cmdbuf.SetViewport(0, viewport);
});
return;
}
@@ -1192,12 +1142,8 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width;
scissor.extent.height = height;
scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) {
const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
std::array<VkRect2D, Maxwell::NumViewports> scissor_list{};
scissor_list.fill(scissor);
const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
cmdbuf.SetScissor(0, scissors);
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) {
cmdbuf.SetScissor(0, scissor);
});
return;
}
@@ -1442,17 +1388,7 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R
if (!state_tracker.TouchPrimitiveRestartEnable()) {
return;
}
bool enable = regs.primitive_restart.enabled != 0;
if (device.IsMoltenVK()) {
enable = true;
} else if (enable) {
const auto topology =
MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology);
enable = IsPrimitiveRestartSupported(device, topology);
}
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable);
});
}
@@ -1791,9 +1727,7 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
auto& dirty{maxwell3d->dirty.flags};
const bool vertex_input_dirty = dirty[Dirty::VertexInput];
const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers];
if (!vertex_input_dirty && !vertex_buffers_dirty) {
if (!dirty[Dirty::VertexInput]) {
return;
}
dirty[Dirty::VertexInput] = false;
@@ -1801,31 +1735,38 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings;
boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes;
const u32 max_attributes =
static_cast<u32>(std::min<size_t>(Maxwell::NumVertexAttributes,
device.GetMaxVertexInputAttributes()));
const u32 max_bindings =
static_cast<u32>(std::min<size_t>(Maxwell::NumVertexArrays,
device.GetMaxVertexInputBindings()));
for (u32 index = 0; index < max_attributes; ++index) {
// There seems to be a bug on Nvidia's driver where updating only higher attributes ends up
// generating dirty state. Track the highest dirty attribute and update all attributes until
// that one.
size_t highest_dirty_attr{};
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
if (dirty[Dirty::VertexAttribute0 + index]) {
highest_dirty_attr = index;
}
}
for (size_t index = 0; index < highest_dirty_attr; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer};
if (attribute.constant || binding >= max_bindings) {
dirty[Dirty::VertexAttribute0 + index] = false;
dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true;
if (!attribute.constant) {
attributes.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
.pNext = nullptr,
.location = static_cast<u32>(index),
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
});
}
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
if (!dirty[Dirty::VertexBinding0 + index]) {
continue;
}
attributes.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
.pNext = nullptr,
.location = index,
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
});
}
dirty[Dirty::VertexBinding0 + index] = false;
for (u32 binding = 0; binding < max_bindings; ++binding) {
const u32 binding{static_cast<u32>(index)};
const auto& input_binding{regs.vertex_streams[binding]};
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
bindings.push_back({
@@ -1837,14 +1778,6 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.divisor = is_instanced ? input_binding.frequency : 1,
});
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
dirty[Dirty::VertexAttribute0 + index] = false;
}
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
dirty[Dirty::VertexBinding0 + index] = false;
}
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes);
});
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -122,7 +122,6 @@ public:
void FlushCommands() override;
void TickFrame() override;
bool AccelerateConditionalRendering() override;
bool HasDrawTransformFeedback() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -324,8 +324,6 @@ void Scheduler::EndRenderPass()
return;
}
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
// Log render pass end
if (Settings::values.gpu_logging_enabled.GetValue() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
@@ -63,11 +63,6 @@ public:
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
/// Returns true when a render pass is currently active in the scheduler state.
bool IsRenderPassActive() const {
return state.renderpass != VK_NULL_HANDLE;
}
/// Update the pipeline to the current execution context.
bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -87,7 +87,6 @@ Flags MakeInvalidationFlags() {
void SetupDirtyViewports(Tables& tables) {
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports);
tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
tables[1][OFF(window_origin)] = Viewports;
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -176,18 +176,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.pViewFormats = view_formats.data(),
};
if (view_formats.size() > 1) {
image_ci.flags |=
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
const bool has_storage_compatible_view =
std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) {
return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
FormatType::Optimal);
});
if (has_storage_compatible_view) {
image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
}
image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (device.IsKhrImageFormatListSupported()) {
image_ci.pNext = &image_format_list;
}
@@ -679,16 +668,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
}
void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
bool emulate_bgr565, bool emulate_a4b4g4r4) {
bool emulate_a4b4g4r4) {
switch (format) {
case PixelFormat::A1B5G5R5_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
break;
case PixelFormat::B5G6R5_UNORM:
if (emulate_bgr565) {
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
}
break;
case PixelFormat::A5B5G5R1_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
break;
@@ -2135,21 +2119,22 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
TryTransformSwizzleIfNeeded(format, swizzle,
device->MustEmulateBGR565(),
!device->IsExt4444FormatsSupported());
!device->IsExt4444FormatsSupported());
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne());
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
const VkImageUsageFlags image_usage = image.UsageFlags();
const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
LOG_WARNING(Render_Vulkan,
"Image view format {} has different usage flags than image format {}", format,
image.info.format);
}
const VkImageViewUsageCreateInfo image_view_usage{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.pNext = nullptr,
.usage = clamped_view_usage,
.usage = ImageUsageFlags(format_info, format),
};
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -2315,18 +2300,23 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
const auto& device = runtime.device;
const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported();
const bool has_format_undefined =
has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported();
const bool has_custom_border_colors =
has_format_undefined && runtime.device.IsCustomBorderColorsSupported();
// Check if custom border colors are supported
const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported();
const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported();
const auto color = tsc.BorderColor();
// Determine border format based on available features:
// - If customBorderColorWithoutFormat is available: use VK_FORMAT_UNDEFINED (most flexible)
// - If only customBorderColors is available: use concrete format (R8G8B8A8_UNORM)
// - If neither is available: use standard border colors (handled by ConvertBorderColor)
const VkFormat border_format = has_format_undefined ? VK_FORMAT_UNDEFINED
: VK_FORMAT_R8G8B8A8_UNORM;
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
.pNext = nullptr,
.customBorderColor = std::bit_cast<VkClearColorValue>(color),
.format = VK_FORMAT_UNDEFINED,
.format = border_format,
};
const void* pnext = nullptr;
if (has_custom_border_colors) {
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -88,13 +88,13 @@ std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformF
return 0;
};
UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream);
Shader::TransformFeedbackVarying varying{
.buffer = static_cast<u32>(buffer),
.stride = layout.stride,
.offset = offset * 4,
.components = 1,
};
varying.stream = layout.stream;
const u32 base_offset = offset;
const auto attribute{get_attribute(offset)};
if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) {
+25 -32
View File
@@ -869,10 +869,6 @@ bool Device::HasTimelineSemaphore() const {
return features.timeline_semaphore.timelineSemaphore;
}
bool Device::MustEmulateBGR565() const {
return Settings::values.emulate_bgr565.GetValue();
}
bool Device::GetSuitability(bool requires_swapchain) {
// Assume we will be suitable.
bool suitable = true;
@@ -923,17 +919,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
FOR_EACH_VK_EXTENSION(EXTENSION);
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
extensions.robustness_2 = true;
} else if (supported_extensions.contains(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.insert(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
extensions.robustness_2 = true;
} else {
extensions.robustness_2 = false;
}
#undef FEATURE_EXTENSION
#undef EXTENSION
@@ -1146,6 +1131,8 @@ bool Device::GetSuitability(bool requires_swapchain) {
if (u32(Settings::values.dyna_state.GetValue()) == 0) {
LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features");
features.custom_border_color.customBorderColors = false;
features.custom_border_color.customBorderColorWithoutFormat = false;
features.extended_dynamic_state.extendedDynamicState = false;
features.extended_dynamic_state2.extendedDynamicState2 = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
@@ -1161,13 +1148,24 @@ bool Device::GetSuitability(bool requires_swapchain) {
void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color
// Enable extension if driver supports it, then check individual features
// - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
// - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED
// If only customBorderColors is available, we must provide a specific format
if (extensions.custom_border_color) {
extensions.custom_border_color =
features.custom_border_color.customBorderColors &&
features.custom_border_color.customBorderColorWithoutFormat;
// Verify that at least customBorderColors is available
if (!features.custom_border_color.customBorderColors) {
LOG_WARNING(Render_Vulkan,
"VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling");
extensions.custom_border_color = false;
}
}
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_KHR_unified_image_layouts
extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts;
RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts,
VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME);
// VK_EXT_depth_bias_control
extensions.depth_bias_control =
@@ -1253,22 +1251,16 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_robustness2
features.robustness2.robustBufferAccess2 = VK_FALSE;
features.robustness2.robustImageAccess2 = VK_FALSE;
extensions.robustness_2 = features.robustness2.nullDescriptor;
const char* robustness2_extension_name =
loaded_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)
? VK_KHR_ROBUSTNESS_2_EXTENSION_NAME
: VK_EXT_ROBUSTNESS_2_EXTENSION_NAME;
extensions.robustness_2 = features.robustness2.robustBufferAccess2 ||
features.robustness2.robustImageAccess2 ||
features.robustness2.nullDescriptor;
RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2,
robustness2_extension_name);
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// Image robustness
extensions.robust_image_access = features.robust_image_access.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.robust_image_access,
features.robust_image_access,
// VK_EXT_image_robustness
extensions.image_robustness = features.image_robustness.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
// VK_KHR_shader_atomic_int64
@@ -1296,7 +1288,8 @@ void Device::RemoveUnsuitableExtensions() {
// VK_EXT_transform_feedback
extensions.transform_feedback =
features.transform_feedback.transformFeedback &&
properties.transform_feedback.maxTransformFeedbackBuffers > 0;
properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
properties.transform_feedback.transformFeedbackQueries;
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
+37 -16
View File
@@ -38,7 +38,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, robust_image_access) \
FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
shader_demote_to_helper_invocation) \
FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
@@ -68,7 +68,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout)
workgroup_memory_explicit_layout) \
FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts)
// Define miscellaneous extensions which may be used by the implementation here.
@@ -123,6 +124,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
@@ -172,11 +174,13 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE_NAME(depth_bias_control, depthBiasExact) \
FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \
FEATURE_NAME(robust_image_access, robustImageAccess) \
FEATURE_NAME(image_robustness, robustImageAccess) \
FEATURE_NAME(index_type_uint8, indexTypeUint8) \
FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
FEATURE_NAME(robustness2, nullDescriptor) \
FEATURE_NAME(robustness2, robustBufferAccess2) \
FEATURE_NAME(robustness2, robustImageAccess2) \
FEATURE_NAME(shader_float16_int8, shaderFloat16) \
FEATURE_NAME(shader_float16_int8, shaderInt8) \
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
@@ -538,17 +542,6 @@ public:
return extensions.transform_feedback;
}
/// Returns true if transform feedback draw commands are supported.
bool IsTransformFeedbackDrawSupported() const {
return extensions.transform_feedback && properties.transform_feedback.transformFeedbackDraw;
}
/// Returns true if transform feedback query types are supported.
bool IsTransformFeedbackQueriesSupported() const {
return extensions.transform_feedback &&
properties.transform_feedback.transformFeedbackQueries;
}
/// Returns true if the device supports VK_EXT_transform_feedback properly.
bool AreTransformFeedbackGeometryStreamsSupported() const {
return features.transform_feedback.geometryStreams;
@@ -559,6 +552,36 @@ public:
return extensions.custom_border_color;
}
/// Returns true if the device supports VK_EXT_image_robustness.
bool IsExtImageRobustnessSupported() const {
return extensions.image_robustness;
}
/// Returns true if robustImageAccess is supported.
bool IsRobustImageAccessSupported() const {
return features.image_robustness.robustImageAccess;
}
/// Returns true if the device supports VK_EXT_robustness2.
bool IsExtRobustness2Supported() const {
return extensions.robustness_2;
}
/// Returns true if robustBufferAccess2 is supported.
bool IsRobustBufferAccess2Supported() const {
return features.robustness2.robustBufferAccess2;
}
/// Returns true if robustImageAccess2 is supported.
bool IsRobustImageAccess2Supported() const {
return features.robustness2.robustImageAccess2;
}
/// Returns true if nullDescriptor is supported.
bool IsNullDescriptorSupported() const {
return features.robustness2.nullDescriptor;
}
/// Returns true if customBorderColors feature is available.
bool IsCustomBorderColorsSupported() const {
return features.custom_border_color.customBorderColors;
@@ -782,8 +805,6 @@ public:
return features.robustness2.nullDescriptor;
}
bool MustEmulateBGR565() const;
bool HasExactDepthBiasControl() const {
return features.depth_bias_control.depthBiasExact;
}
@@ -123,7 +123,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdEndDebugUtilsLabelEXT);
X(vkCmdFillBuffer);
X(vkCmdPipelineBarrier);
X(vkCmdResetQueryPool);
X(vkCmdPushConstants);
X(vkCmdPushDescriptorSetWithTemplateKHR);
X(vkCmdSetBlendConstants);
@@ -225,7 +225,6 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
PFN_vkCmdFillBuffer vkCmdFillBuffer{};
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
PFN_vkCmdResetQueryPool vkCmdResetQueryPool{};
PFN_vkCmdPushConstants vkCmdPushConstants{};
PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{};
PFN_vkCmdResolveImage vkCmdResolveImage{};
@@ -1169,10 +1168,6 @@ public:
dld->vkCmdEndQuery(handle, query_pool, query);
}
void ResetQueryPool(VkQueryPool query_pool, u32 first_query, u32 query_count) const noexcept {
dld->vkCmdResetQueryPool(handle, query_pool, first_query, query_count);
}
void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first,
Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept {
dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(),
+24 -20
View File
@@ -23,18 +23,27 @@ DataDialog::DataDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique<U
// TODO: Should we make this a single widget that pulls data from a model?
#define WIDGET(label, name) \
ui->pages->addTab(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
QtCommon::StringLookup::DataManager##name##Tooltip, \
QStringLiteral(#name), this), \
label);
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
QtCommon::StringLookup::DataManager##name##Tooltip, \
QStringLiteral(#name), this)); \
ui->labels->addItem(label);
WIDGET(tr("Shaders"), Shaders)
WIDGET(tr("User NAND"), UserNand)
WIDGET(tr("System NAND"), SysNand)
WIDGET(tr("UserNAND"), UserNand)
WIDGET(tr("SysNAND"), SysNand)
WIDGET(tr("Mods"), Mods)
WIDGET(tr("Saves"), Saves)
#undef WIDGET
connect(ui->labels, &QListWidget::itemSelectionChanged, this, [this]() {
const auto items = ui->labels->selectedItems();
if (items.isEmpty()) {
return;
}
ui->page->setCurrentIndex(ui->labels->row(items[0]));
});
}
DataDialog::~DataDialog() = default;
@@ -62,29 +71,25 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
}
void DataWidget::clear() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
QtCommon::Content::ClearDataDir(m_dir, user_id.value());
std::string user_id = selectProfile();
QtCommon::Content::ClearDataDir(m_dir, user_id);
scan();
}
void DataWidget::open() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
std::string user_id = selectProfile();
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id))));
}
void DataWidget::upload() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
QtCommon::Content::ExportDataDir(m_dir, user_id.value(), m_exportName);
std::string user_id = selectProfile();
QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName);
}
void DataWidget::download() {
std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
QtCommon::Content::ImportDataDir(m_dir, user_id.value(), std::bind(&DataWidget::scan, this));
std::string user_id = selectProfile();
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
}
void DataWidget::scan() {
@@ -103,11 +108,10 @@ void DataWidget::scan() {
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); }));
}
std::optional<std::string> DataWidget::selectProfile() {
std::string DataWidget::selectProfile() {
std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = GetProfileIDString();
if (user_id.empty()) return std::nullopt;
}
return user_id;
+1 -1
View File
@@ -45,7 +45,7 @@ private:
FrontendCommon::DataManager::DataDir m_dir;
const QString m_exportName;
std::optional<std::string> selectProfile();
std::string selectProfile();
};
#endif // DATA_DIALOG_H
+24 -2
View File
@@ -27,9 +27,31 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<widget class="QTabWidget" name="pages">
<widget class="QListWidget" name="labels">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="page">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>275</width>
<height>200</height>
</size>
</property>
<property name="currentIndex">
<number>-1</number>
</property>