Compare commits

..

14 Commits

Author SHA1 Message Date
CamilleLaVey 95759ae726 [TEST] Quick change on depth's handling 2026-08-12 01:30:23 -04:00
CamilleLaVey dba3bfe74b [TEST] Depth stencil resolve + MSAA depth stencil 2026-08-12 01:19:52 -04:00
CamilleLaVey 6d9e4b97a6 [TEST] MSAA uploads 2026-08-12 00:44:14 -04:00
CamilleLaVey c14bfc3d5e [TEST] Fix vkFormat accessors 2026-08-11 23:25:46 -04:00
CamilleLaVey a238536d6d [TEST] Geometry shader changes + barrier fixes 2026-08-11 23:07:23 -04:00
CamilleLaVey 9fe8674760 [TEST] Fix TFB subdependency 2026-08-11 21:30:31 -04:00
CamilleLaVey 97547657b5 [TEST] Fix missing barrier + removal of required aligments checks inside swizzle textures 2026-08-11 21:00:58 -04:00
CamilleLaVey f73164b191 [TEST] Adjustments on pipeline hash collisions 2026-08-11 20:34:08 -04:00
CamilleLaVey 35c420582f [TEST] Remove subpass dependency 2026-08-11 20:10:53 -04:00
CamilleLaVey b2b7a34957 [TEST] Ported PSO optimizations from tiled-gpu-v2 2026-08-10 03:54:18 -04:00
CamilleLaVey d7a564b088 [TEST] Another bind vertex buffer optimization from previous handling 2026-08-10 03:30:58 -04:00
CamilleLaVey 1d57d4a73d [TEST] Playing with SGSR 2026-08-10 02:57:05 -04:00
CamilleLaVey 3af03332cc [TEST] fast path on swizzle decoding 2026-08-10 01:41:21 -04:00
CamilleLaVey a6e506c328 [TEST] Bring the unswizzle work from tiled-gpu-v2 2026-08-10 01:29:27 -04:00
51 changed files with 2188 additions and 604 deletions
@@ -30,6 +30,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"), ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"), USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
SYNC_MEMORY_OPERATIONS("sync_memory_operations"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
BUFFER_REORDER_DISABLE("disable_buffer_reorder"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),
@@ -586,6 +586,7 @@ abstract class SettingsItem(
IntSetting.FSR_SHARPENING_SLIDER, IntSetting.FSR_SHARPENING_SLIDER,
titleId = R.string.fsr_sharpness, titleId = R.string.fsr_sharpness,
descriptionId = R.string.fsr_sharpness_description, descriptionId = R.string.fsr_sharpness_description,
max = 200,
units = "%" units = "%"
) )
) )
@@ -808,6 +809,13 @@ abstract class SettingsItem(
descriptionId = R.string.enable_buffer_history_description descriptionId = R.string.enable_buffer_history_description
) )
) )
put(
SwitchSetting(
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
titleId = R.string.enable_gpu_buffer_readback,
descriptionId = R.string.enable_gpu_buffer_readback_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS, BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
@@ -294,6 +294,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key) add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
add(HeaderSetting(R.string.hacks)) add(HeaderSetting(R.string.hacks))
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
container, container,
IntSetting.FSR_SHARPENING_SLIDER, IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0, minValue = 0,
maxValue = 100, maxValue = 200,
units = "%" units = "%"
) )
} }
@@ -499,6 +499,8 @@
<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">تمكين سجل التخزين المؤقت</string>
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string> <string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string> <string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string> <string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
@@ -491,6 +491,8 @@
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string> <string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
<string name="enable_buffer_history">Activar el historial del búfer</string> <string name="enable_buffer_history">Activar el historial del búfer</string>
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string> <string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string> <string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string> <string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
@@ -489,6 +489,8 @@
<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">启用缓冲区历史</string>
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string> <string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string> <string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string> <string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
@@ -505,6 +505,8 @@
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="enable_buffer_history">Enable buffer history</string> <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="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="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</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 (25.3 and below).</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 (25.3 and below).</string>
+7
View File
@@ -576,6 +576,13 @@ struct Values {
false, false,
#endif #endif
"rescale_hack", Category::RendererHacks}; "rescale_hack", Category::RendererHacks};
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
false,
"enable_gpu_buffer_readback",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks}; Category::RendererHacks};
@@ -88,25 +88,10 @@ Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
} }
Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) { Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
// Ensure the display name is null-terminated // Ensure the display name is null-terminated
display_name[display_name.size() - 1] = '\0'; display_name[display_name.size() - 1] = '\0';
// According to switchbrew, only "Default", "External", "Edid", "Internal" and "Null" are valid LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
const std::array<std::string_view, 5> valid_names = {
"Default", "External", "Edid", "Internal", "Null"
};
bool valid_name = false;
for (const auto& name : valid_names) {
if (name == display_name.data()) {
valid_name = true;
break;
}
}
R_UNLESS(valid_name, ResultOperationFailed);
R_RETURN(m_container->OpenDisplay(out_display_id, display_name)); R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
} }
@@ -223,6 +223,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting.")); tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"), INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting.")); tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"), INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
tr("May reduce shader stutter.")); tr("May reduce shader stutter."));
INSERT(Settings, gpu_clock, tr("GPU Clocks"), INSERT(Settings, gpu_clock, tr("GPU Clocks"),
@@ -50,12 +50,6 @@ constexpr std::array RGBA_LUT{
R | G | B | A, // R | G | B | A, //
}; };
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
template <typename... Args> template <typename... Args>
IR::Value Composite(TranslatorVisitor& v, Args... regs) { IR::Value Composite(TranslatorVisitor& v, Args... regs) {
return v.ir.CompositeConstruct(v.F(regs)...); return v.ir.CompositeConstruct(v.F(regs)...);
@@ -86,67 +80,53 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
case 3: // 2D.LL case 3: // 2D.LL
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {}, return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
info); info);
case 4: // 2D.DC case 4: // 2D.DC
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
{}, {}, {}, info); {}, {}, {}, info);
case 5: // 2D.LL.DC case 5: // 2D.LL.DC
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
v.F(reg_b + 1), v.F(reg_b), {}, info); v.F(reg_b + 1), v.F(reg_b), {}, info);
case 6: // 2D.LZ.DC case 6: // 2D.LZ.DC
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
zero, {}, info); zero, {}, info);
case 7: // ARRAY_2D case 7: // ARRAY_2D
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleImplicitLod( return v.ir.ImageSampleImplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
{}, {}, {}, info); {}, {}, {}, info);
case 8: // ARRAY_2D.LZ case 8: // ARRAY_2D.LZ
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleExplicitLod( return v.ir.ImageSampleExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
zero, {}, info); zero, {}, info);
case 9: // ARRAY_2D.LZ.DC case 9: // ARRAY_2D.LZ.DC
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod( return v.ir.ImageSampleDrefExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
v.F(reg_b + 1), zero, {}, info); v.F(reg_b + 1), zero, {}, info);
case 10: // 3D case 10: // 3D
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color3D); info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info); {}, info);
case 11: // 3D.LZ case 11: // 3D.LZ
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color3D); info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {}, return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
info); info);
case 12: // CUBE case 12: // CUBE
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorCube); info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info); {}, info);
case 13: // CUBE.LL case 13: // CUBE.LL
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::ColorCube); info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
v.F(reg_b + 1), {}, info); v.F(reg_b + 1), {}, info);
@@ -187,12 +167,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
case 0: case 0:
return texs.dest_reg_a; return texs.dest_reg_a;
case 1: case 1:
CheckAlignment(texs.dest_reg_a, 2);
return texs.dest_reg_a + 1; return texs.dest_reg_a + 1;
case 2: case 2:
return texs.dest_reg_b; return texs.dest_reg_b;
case 3: case 3:
CheckAlignment(texs.dest_reg_b, 2);
return texs.dest_reg_b + 1; return texs.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
@@ -34,12 +34,6 @@ union Encoding {
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
}; };
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) { IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
const IR::U32 value{v.X(reg)}; const IR::U32 value{v.X(reg)};
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true), return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
@@ -60,18 +54,15 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0); info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
IR::Value coords; IR::Value coords;
if (tld4s.aoffi != 0) { if (tld4s.aoffi != 0) {
CheckAlignment(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::Value offset = MakeOffset(v, reg_b); IR::Value offset = MakeOffset(v, reg_b);
if (tld4s.dc != 0) { if (tld4s.dc != 0) {
CheckAlignment(reg_b, 2);
IR::F32 dref = v.F(reg_b + 1); IR::F32 dref = v.F(reg_b + 1);
return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info); return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
} }
return v.ir.ImageGather(handle, coords, offset, {}, info); return v.ir.ImageGather(handle, coords, offset, {}, info);
} }
if (tld4s.dc != 0) { if (tld4s.dc != 0) {
CheckAlignment(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::F32 dref = v.F(reg_b); IR::F32 dref = v.F(reg_b);
return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info); return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
@@ -86,12 +77,10 @@ IR::Reg RegStoreComponent32(u64 insn, size_t index) {
case 0: case 0:
return tlds4.dest_reg_a; return tlds4.dest_reg_a;
case 1: case 1:
CheckAlignment(tlds4.dest_reg_a, 2);
return tlds4.dest_reg_a + 1; return tlds4.dest_reg_a + 1;
case 2: case 2:
return tlds4.dest_reg_b; return tlds4.dest_reg_b;
case 3: case 3:
CheckAlignment(tlds4.dest_reg_b, 2);
return tlds4.dest_reg_b + 1; return tlds4.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
@@ -55,12 +55,6 @@ union Encoding {
BitField<53, 4, u64> encoding; BitField<53, 4, u64> encoding;
}; };
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) { IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
const IR::U32 value{v.X(reg)}; const IR::U32 value{v.X(reg)};
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
@@ -92,38 +86,31 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b));
break; break;
case 4: case 4:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
offsets = MakeOffset(v, reg_b); offsets = MakeOffset(v, reg_b);
break; break;
case 5: case 5:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b); lod = v.X(reg_b);
break; break;
case 6: case 6:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
multisample = v.X(reg_b); multisample = v.X(reg_b);
break; break;
case 7: case 7:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color3D; texture_type = Shader::TextureType::Color3D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b));
break; break;
case 8: { case 8: {
CheckAlignment(reg_b, 2);
const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))}; const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))};
texture_type = Shader::TextureType::ColorArray2D; texture_type = Shader::TextureType::ColorArray2D;
coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array); coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array);
break; break;
} }
case 12: case 12:
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b); lod = v.X(reg_b);
@@ -166,12 +153,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
case 0: case 0:
return tlds.dest_reg_a; return tlds.dest_reg_a;
case 1: case 1:
CheckAlignment(tlds.dest_reg_a, 2);
return tlds.dest_reg_a + 1; return tlds.dest_reg_a + 1;
case 2: case 2:
return tlds.dest_reg_b; return tlds.dest_reg_b;
case 3: case 3:
CheckAlignment(tlds.dest_reg_b, 2);
return tlds.dest_reg_b + 1; return tlds.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
@@ -169,11 +169,36 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
return mapping; return mapping;
} }
struct PassthroughVertices {
u32 count;
u32 first;
u32 stride;
};
PassthroughVertices GetPassthroughVertices(InputTopology input_topology) {
switch (input_topology) {
case InputTopology::Points:
return {1, 0, 1};
case InputTopology::Lines:
return {2, 0, 1};
case InputTopology::LinesAdjacency:
return {2, 1, 1};
case InputTopology::Triangles:
return {3, 0, 1};
case InputTopology::TrianglesAdjacency:
return {3, 0, 2};
}
return {3, 0, 1};
}
void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
const Shader::VaryingState& passthrough_mask, const Shader::VaryingState& passthrough_mask,
bool passthrough_position, bool passthrough_position,
std::optional<IR::Attribute> passthrough_layer_attr) { std::optional<IR::Attribute> passthrough_layer_attr,
for (u32 i = 0; i < program.output_vertices; i++) { InputTopology input_topology) {
const PassthroughVertices vertices{GetPassthroughVertices(input_topology)};
for (u32 vertex = 0; vertex < vertices.count; vertex++) {
const u32 i = vertices.first + vertex * vertices.stride;
// Assign generics from input // Assign generics from input
for (u32 j = 0; j < 32; j++) { for (u32 j = 0; j < 32; j++) {
if (!passthrough_mask.Generic(j)) { if (!passthrough_mask.Generic(j)) {
@@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
ir.EndPrimitive(ir.Imm32(0)); ir.EndPrimitive(ir.Imm32(0));
} }
u32 GetOutputTopologyVertices(OutputTopology output_topology) { void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info,
switch (output_topology) { InputTopology input_topology) {
case OutputTopology::PointList:
return 1;
case OutputTopology::LineStrip:
return 2;
default:
return 3;
}
}
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
for (IR::Block* const block : program.blocks) { for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
if (inst.GetOpcode() == IR::Opcode::Epilogue) { if (inst.GetOpcode() == IR::Opcode::Epilogue) {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
EmitGeometryPassthrough( EmitGeometryPassthrough(
ir, program, program.info.passthrough, ir, program, program.info.passthrough,
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}); program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {},
input_topology);
} }
} }
} }
@@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
} // Anonymous namespace } // Anonymous namespace
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info,
InputTopology input_topology) {
HostTranslateInfo normalized_host_info{host_info}; HostTranslateInfo normalized_host_info{host_info};
normalized_host_info.ApplyDescriptorLimitPolicy(); normalized_host_info.ApplyDescriptorLimitPolicy();
@@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
} }
if (!normalized_host_info.support_geometry_shader_passthrough) { if (!normalized_host_info.support_geometry_shader_passthrough) {
program.output_vertices = GetOutputTopologyVertices(program.output_topology); program.output_vertices = GetPassthroughVertices(input_topology).count;
LowerGeometryPassthrough(program, normalized_host_info); LowerGeometryPassthrough(program, normalized_host_info, input_topology);
program.is_geometry_passthrough = false;
} }
} }
break; break;
@@ -414,11 +432,12 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, ObjectPool<IR::Block>& block_pool,
const HostTranslateInfo& host_info, const HostTranslateInfo& host_info,
IR::Program& source_program, IR::Program& source_program,
Shader::OutputTopology output_topology) { Shader::OutputTopology output_topology,
InputTopology input_topology) {
IR::Program program; IR::Program program;
program.stage = Stage::Geometry; program.stage = Stage::Geometry;
program.output_topology = output_topology; program.output_topology = output_topology;
program.output_vertices = GetOutputTopologyVertices(output_topology); program.output_vertices = GetPassthroughVertices(input_topology).count;
program.is_geometry_passthrough = false; program.is_geometry_passthrough = false;
program.info.loads.mask = source_program.info.stores.mask; program.info.loads.mask = source_program.info.stores.mask;
@@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
IR::IREmitter ir{*current_block}; IR::IREmitter ir{*current_block};
EmitGeometryPassthrough(ir, program, program.info.stores, true, EmitGeometryPassthrough(ir, program, program.info.stores, true,
source_program.info.emulated_layer); source_program.info.emulated_layer, input_topology);
IR::Block* return_block{block_pool.Create(inst_pool)}; IR::Block* return_block{block_pool.Create(inst_pool)};
IR::IREmitter{*return_block}.Epilogue(); IR::IREmitter{*return_block}.Epilogue();
@@ -18,7 +18,8 @@ namespace Shader::Maxwell {
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, [[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, Environment& env, ObjectPool<IR::Block>& block_pool, Environment& env,
Flow::CFG& cfg, const HostTranslateInfo& host_info); Flow::CFG& cfg, const HostTranslateInfo& host_info,
InputTopology input_topology);
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
Environment& env_vertex_b); Environment& env_vertex_b);
@@ -32,6 +33,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf
ObjectPool<IR::Block>& block_pool, ObjectPool<IR::Block>& block_pool,
const HostTranslateInfo& host_info, const HostTranslateInfo& host_info,
IR::Program& source_program, IR::Program& source_program,
Shader::OutputTopology output_topology); Shader::OutputTopology output_topology,
InputTopology input_topology);
} // namespace Shader::Maxwell } // namespace Shader::Maxwell
+45 -50
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <bit>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
@@ -809,46 +810,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
if (use_optimized_vertex_buffers) { if (use_optimized_vertex_buffers) {
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
u32 enabled_mask = enabled_vertex_buffers_mask; const u32 enabled_mask = enabled_vertex_buffers_mask;
HostBindings<Buffer> bindings{}; bool any_dirty = false;
u32 last_index = (std::numeric_limits<u32>::max)(); u32 pending_mask = enabled_mask;
const auto flush_bindings = [&]() { while (pending_mask != 0) {
if (bindings.buffers.empty()) { const u32 index = std::countr_zero(pending_mask);
return; pending_mask &= (pending_mask - 1);
}
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
runtime.BindVertexBuffers(bindings);
bindings = HostBindings<Buffer>{};
last_index = (std::numeric_limits<u32>::max)();
};
while (enabled_mask != 0) {
const u32 index = std::countr_zero(enabled_mask);
enabled_mask &= (enabled_mask - 1);
const Binding& binding = VertexBufferSlot(index); const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id); TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.device_addr, binding.size); SynchronizeBuffer(buffer, binding.device_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) { any_dirty |= flags[Dirty::VertexBuffer0 + index];
flush_bindings(); }
continue; if (enabled_mask == 0 || !any_dirty) {
} return;
}
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
HostBindings<Buffer> bindings{};
bindings.min_index = min_index;
bindings.max_index = max_index;
for (u32 index = min_index; index < max_index; ++index) {
flags[Dirty::VertexBuffer0 + index] = false; flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
if ((enabled_mask & (1u << index)) == 0) {
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
bindings.offsets.push_back(0);
bindings.sizes.push_back(0);
bindings.strides.push_back(stride);
continue;
}
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
const u32 offset = buffer.Offset(binding.device_addr); const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size); buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
flush_bindings();
}
if (bindings.buffers.empty()) {
bindings.min_index = index;
}
bindings.buffers.push_back(&buffer); bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset); bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size); bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride); bindings.strides.push_back(stride);
last_index = index;
} }
flush_bindings(); runtime.BindVertexBuffers(bindings);
} else { } else {
HostBindings<typename P::Buffer> host_bindings; HostBindings<typename P::Buffer> host_bindings;
bool any_valid{false}; bool any_valid{false};
@@ -1448,8 +1449,8 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
const BufferId buffer_id = page_table[page]; const BufferId buffer_id = page_table[page];
if (buffer_id) { if (buffer_id) {
Buffer& buffer = slot_buffers[buffer_id]; Buffer& buffer = slot_buffers[buffer_id];
WaitForGpuFenceIfNeeded(buffer);
if (buffer.IsInBounds(device_addr, size)) { if (buffer.IsInBounds(device_addr, size)) {
SynchronizeBufferIfNeeded(buffer);
return buffer_id; return buffer_id;
} }
} }
@@ -1457,28 +1458,16 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
} }
template <class P> template <class P>
void BufferCache<P>::SynchronizeBufferIfNeeded(Buffer& buffer) { void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate(); if constexpr (!IS_OPENGL) {
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict(); const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
const bool should_sync = gpu_fence_accurate || gpu_fence_strict; const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
if (should_sync) { if (gpu_fence_accurate || gpu_fence_strict) {
if (gpu_fence_accurate) { const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
if constexpr (!IS_OPENGL) { const u64 buffer_tick = buffer.getWriteTick();
const bool should_wait = buffer.getWriteTick() > runtime.KnownGpuTick() + 3; const u64 gpu_tick = runtime.KnownGpuTick();
if (should_wait) { if (buffer_tick > gpu_tick + gpu_tick_delay) {
runtime.Wait(buffer.getWriteTick()); runtime.Wait(buffer_tick);
}
}
} else if (gpu_fence_strict) {
bool should_download = true;
if constexpr (!IS_OPENGL) {
should_download = buffer.getWriteTick() > runtime.KnownGpuTick();
if (should_download) {
runtime.Wait(buffer.getWriteTick());
}
}
if (should_download) {
DownloadBufferMemory(buffer);
} }
} }
} }
@@ -1700,6 +1689,9 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
if (immediate_buffer.empty()) { if (immediate_buffer.empty()) {
immediate_buffer = ImmediateBuffer(largest_copy); immediate_buffer = ImmediateBuffer(largest_copy);
} }
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
DownloadBufferMemory(buffer, device_addr, copy.size);
}
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size); device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
upload_span = immediate_buffer.subspan(0, copy.size); upload_span = immediate_buffer.subspan(0, copy.size);
} }
@@ -1718,6 +1710,9 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
for (BufferCopy& copy : copies) { for (BufferCopy& copy : copies) {
u8* const src_pointer = staging_pointer.data() + copy.src_offset; u8* const src_pointer = staging_pointer.data() + copy.src_offset;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
DownloadBufferMemory(buffer, device_addr, copy.size);
}
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size); device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
// Apply the staging offset // Apply the staging offset
copy.src_offset += upload_staging.offset; copy.src_offset += upload_staging.offset;
@@ -435,8 +435,6 @@ private:
bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size); bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size);
void SynchronizeBufferIfNeeded(Buffer& buffer);
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies); std::span<BufferCopy> copies);
@@ -17,11 +17,13 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp ${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
@@ -32,6 +34,8 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert ${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#extension GL_EXT_shader_16bit_storage : require
#extension GL_EXT_shader_8bit_storage : require
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint layer_stride;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += pos.z * pc.layer_stride;
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint slice_size;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
uint block_depth;
uint block_depth_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += (pos.z >> pc.block_depth) * pc.slice_size;
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2D img_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(img_in, coord, 0).r;
}
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform usampler2D stencil_tex;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
}
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
vec2 scale; vec2 scale;
vec2 size; vec2 size;
vec2 resize_factor; vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness; float edge_sharpness;
}; };
layout(location = 0) out highp vec2 texcoord; layout(location = 0) out highp vec2 texcoord;
@@ -15,5 +16,5 @@ void main() {
float x = float((gl_VertexIndex & 1) << 2); float x = float((gl_VertexIndex & 1) << 2);
float y = float((gl_VertexIndex & 2) << 1); float y = float((gl_VertexIndex & 2) << 1);
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f); gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
texcoord = vec2(x, y) * abs(resize_factor) * 0.5; texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
} }
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
vec2 scale; vec2 scale;
vec2 size; vec2 size;
vec2 resize_factor; vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness; float edge_sharpness;
}; };
layout(set = 0, binding = 0) uniform sampler2D sampler0; layout(set = 0, binding = 0) uniform sampler2D sampler0;
@@ -13,6 +13,7 @@
layout( push_constant ) uniform constants { layout( push_constant ) uniform constants {
vec4 ViewportInfo[1]; vec4 ViewportInfo[1];
vec2 ResizeFactor; vec2 ResizeFactor;
vec2 CropOffset;
float EdgeSharpness; float EdgeSharpness;
}; };
layout(set = 0, binding = 0) uniform sampler2D ps0; layout(set = 0, binding = 0) uniform sampler2D ps0;
@@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo
} }
} }
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return Shader::InputTopology::Points;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
return Shader::InputTopology::Lines;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return Shader::InputTopology::LinesAdjacency;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return Shader::InputTopology::TrianglesAdjacency;
default:
return Shader::InputTopology::Triangles;
}
}
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
const Shader::IR::Program& program, const Shader::IR::Program& program,
const Shader::IR::Program* previous_program, const Shader::IR::Program* previous_program,
@@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
default: default:
break; break;
} }
switch (key.gs_input_topology) { info.input_topology = MaxwellToInputTopology(key.gs_input_topology);
case Maxwell::PrimitiveTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
case Maxwell::PrimitiveTopology::Patches:
info.input_topology = Shader::InputTopology::Triangles;
break;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.glasm_use_storage_buffers = glasm_use_storage_buffers; info.glasm_use_storage_buffers = glasm_use_storage_buffers;
return info; return info;
} }
@@ -483,8 +476,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
&& index == u32(Maxwell::ShaderType::Geometry); && index == u32(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) { if (key.unique_hashes[index] == 0 && is_emulated_stage) {
auto topology = MaxwellToOutputTopology(key.gs_input_topology); auto topology = MaxwellToOutputTopology(key.gs_input_topology);
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, programs[index] =
*layer_source_program, topology); GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology,
MaxwellToInputTopology(key.gs_input_topology));
continue; continue;
} }
if (key.unique_hashes[index] == 0) { if (key.unique_hashes[index] == 0) {
@@ -502,13 +497,15 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
if (!uses_vertex_a || index != 1) { if (!uses_vertex_a || index != 1) {
// Normal path // Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.gs_input_topology));
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
} else { } else {
// VertexB path when VertexA is present. // VertexB path when VertexA is present.
auto& program_va{programs[0]}; auto& program_va{programs[0]};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.gs_input_topology))};
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
} }
@@ -597,7 +594,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash); env.Dump(hash, key.unique_hash);
} }
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
Shader::InputTopology::Points)};
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
Shader::RuntimeInfo info; Shader::RuntimeInfo info;
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
+293 -13
View File
@@ -22,6 +22,8 @@
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h" #include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h" #include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" #include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -519,7 +521,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
} }
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image, [[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
VkFormat format, u32 base_level) { VkFormat format, u32 base_level,
VkImageAspectFlags aspect_mask) {
return device.CreateImageView(VkImageViewCreateInfo{ return device.CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@@ -534,7 +537,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
.a = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY,
}, },
.subresourceRange{ .subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = aspect_mask,
.baseMipLevel = base_level, .baseMipLevel = base_level,
.levelCount = 1, .levelCount = 1,
.baseArrayLayer = 0, .baseArrayLayer = 0,
@@ -586,6 +589,10 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
one_texture_set_layout.address(), one_texture_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))), PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
msaa_copy_depth_stencil_pipeline_layout(
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
two_textures_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)), blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)), blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
@@ -610,6 +617,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)), convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)), convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_stencil_frag(
device.IsExtShaderStencilExportSupported()
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {} nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -920,10 +933,12 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
ASSERT(copy.dst_subresource.num_layers == 1); ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view = vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level)); static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_COLOR_BIT);
vk::ImageView dst_view = vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level)); static_cast<u32>(copy.dst_subresource.base_level),
VK_IMAGE_ASPECT_COLOR_BIT);
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y}; const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height}; const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{ const VkRect2D render_area{
@@ -1191,7 +1206,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *blit_color_pipelines.back(); return *blit_color_pipelines.back();
} }
@@ -1223,7 +1238,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *blit_depth_stencil_pipelines.back(); return *blit_depth_stencil_pipelines.back();
} }
@@ -1276,7 +1291,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *clear_color_pipelines.back(); return *clear_color_pipelines.back();
} }
@@ -1332,7 +1347,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *clear_stencil_pipelines.back(); return *clear_stencil_pipelines.back();
} }
@@ -1375,7 +1390,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *blit_msaa_color_pipelines.back(); return *blit_msaa_color_pipelines.back();
} }
@@ -1413,10 +1428,202 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *pipelines.back(); return *pipelines.back();
} }
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies,
bool copy_stencil) {
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
msaa_copy_resources.pop_front();
}
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
const s32 scale_x = 1 << samples_x;
const s32 scale_y = 1 << samples_y;
const VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
renderpass_key.depth_format = dst_format;
renderpass_key.samples = samples;
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
const MSAACopyPipelineKey key{
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = false,
};
const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil);
const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
: *msaa_copy_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkFormat src_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
const VkFormat dst_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
const VkImageAspectFlags attachment_aspect =
VideoCore::Surface::GetFormatType(dst_format) ==
VideoCore::Surface::SurfaceType::DepthStencil
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: VK_IMAGE_ASPECT_DEPTH_BIT;
for (const VideoCommon::ImageCopy& copy : copies) {
ASSERT(copy.src_subresource.base_layer == 0);
ASSERT(copy.src_subresource.num_layers == 1);
ASSERT(copy.dst_subresource.base_layer == 0);
ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_DEPTH_BIT);
vk::ImageView src_stencil_view =
copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_STENCIL_BIT)
: vk::ImageView{};
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level),
attachment_aspect);
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
.offset = dst_offset,
.extent = dst_extent,
};
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = renderpass,
.attachmentCount = 1,
.pAttachments = dst_view.address(),
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
.layers = 1,
});
const MSAACopyPushConstants push_constants{
.dst_offset = {dst_offset.x, dst_offset.y},
.src_offset = {copy.src_offset.x, copy.src_offset.y},
.scale = {scale_x, scale_y},
};
scheduler.RequestOutsideRenderPassOperationContext();
const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE;
scheduler.Record([this, pipeline, layout, sampler, renderpass,
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
src_stencil_handle, src = src_image, dst = dst_image, render_area,
attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) {
const VkImageSubresourceRange src_range{
.aspectMask = attachment_aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src,
.subresourceRange = src_range,
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = src_range,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
0, nullptr, nullptr, pre_barriers);
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = renderpass,
.framebuffer = framebuffer_handle,
.renderArea = render_area,
.clearValueCount = 0,
.pClearValues = nullptr,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
const VkDescriptorSet descriptor_set =
src_stencil_handle != VK_NULL_HANDLE
? two_textures_descriptor_allocator.Commit()
: one_texture_descriptor_allocator.Commit();
if (src_stencil_handle != VK_NULL_HANDLE) {
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
src_stencil_handle);
} else {
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
}
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const VkViewport viewport{
.x = static_cast<float>(render_area.offset.x),
.y = static_cast<float>(render_area.offset.y),
.width = static_cast<float>(render_area.extent.width),
.height = static_cast<float>(render_area.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, render_area);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = src_range,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, post_barrier);
});
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_view),
.dst_view = std::move(dst_view),
.framebuffer = std::move(framebuffer),
});
if (copy_stencil) {
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_stencil_view),
.dst_view = vk::ImageView{},
.framebuffer = vk::Framebuffer{},
});
}
}
}
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) { VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
const auto it = std::ranges::find(msaa_copy_keys, key); const auto it = std::ranges::find(msaa_copy_keys, key);
if (it != msaa_copy_keys.end()) { if (it != msaa_copy_keys.end()) {
@@ -1458,10 +1665,83 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
})); }, device.StaticPipelineCache()));
return *msaa_copy_pipelines.back(); return *msaa_copy_pipelines.back();
} }
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil) {
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
const auto it = std::ranges::find(keys, key);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(key);
const std::array stages =
MakeStages(*clear_color_vert, copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
: *convert_non_msaa_to_msaa_depth_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.rasterizationSamples = key.samples,
.sampleShadingEnable = VK_TRUE,
.minSampleShading = 1.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0xFF,
.writeMask = 0xFF,
.reference = 0,
};
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &multisample_ci,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
: *msaa_copy_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
return *pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false); ConvertPipeline(pipeline, renderpass, false);
} }
@@ -1499,7 +1779,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
}); }, device.StaticPipelineCache());
} }
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
@@ -1542,7 +1822,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
.subpass = 0, .subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0, .basePipelineIndex = 0,
}); }, device.StaticPipelineCache());
} }
} // namespace Vulkan } // namespace Vulkan
@@ -116,6 +116,11 @@ public:
VideoCore::Surface::PixelFormat src_format, u32 num_samples, VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa); std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil);
private: private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view); const ImageView& src_image_view);
@@ -131,6 +136,9 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline( [[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key); const BlitDepthStencilPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil); bool resolve_stencil);
@@ -162,6 +170,7 @@ private:
vk::PipelineLayout two_textures_pipeline_layout; vk::PipelineLayout two_textures_pipeline_layout;
vk::PipelineLayout clear_color_pipeline_layout; vk::PipelineLayout clear_color_pipeline_layout;
vk::PipelineLayout msaa_copy_pipeline_layout; vk::PipelineLayout msaa_copy_pipeline_layout;
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
vk::ShaderModule full_screen_vert; vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag; vk::ShaderModule blit_color_to_color_frag;
vk::ShaderModule blit_color_msaa_frag; vk::ShaderModule blit_color_msaa_frag;
@@ -180,6 +189,8 @@ private:
vk::ShaderModule convert_s8d24_to_abgr8_frag; vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::ShaderModule convert_msaa_to_non_msaa_frag; vk::ShaderModule convert_msaa_to_non_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_frag; vk::ShaderModule convert_non_msaa_to_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
vk::Sampler linear_sampler; vk::Sampler linear_sampler;
vk::Sampler nearest_sampler; vk::Sampler nearest_sampler;
@@ -193,6 +204,10 @@ private:
std::vector<vk::Pipeline> clear_stencil_pipelines; std::vector<vk::Pipeline> clear_stencil_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_keys; std::vector<MSAACopyPipelineKey> msaa_copy_keys;
std::vector<vk::Pipeline> msaa_copy_pipelines; std::vector<vk::Pipeline> msaa_copy_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys; std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines; std::vector<vk::Pipeline> blit_msaa_color_pipelines;
std::vector<VkRenderPass> resolve_depth_keys; std::vector<VkRenderPass> resolve_depth_keys;
+18 -14
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/div_ceil.h" #include "common/div_ceil.h"
#include "common/settings.h" #include "common/settings.h"
@@ -17,7 +19,7 @@
namespace Vulkan { namespace Vulkan {
using PushConstants = std::array<u32, 4 + 2 + 1>; using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir) SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
: m_memory_allocator{memory_allocator} : m_memory_allocator{memory_allocator}
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
const f32 input_image_width = f32(input_image_extent.width); const f32 input_image_width = f32(input_image_extent.width);
const f32 input_image_height = f32(input_image_extent.height); const f32 input_image_height = f32(input_image_extent.height);
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
// expected [0, 2] static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; const f32 edge_sharpness =
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
// p * input = [0,1024], [0,768]
// layout( push_constant ) uniform constants { // layout( push_constant ) uniform constants {
// highp vec4 ViewportInfo[1]; // highp vec4 ViewportInfo[1];
// highp vec2 ResizeFactor; // highp vec2 ResizeFactor;
// highp vec2 CropOffset;
// highp float EdgeSharpness; // highp float EdgeSharpness;
// }; // };
PushConstants viewport_con{}; PushConstants viewport_con{};
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width)); viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height)); viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width)); viewport_con[2] = std::bit_cast<u32>(input_image_width);
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height)); viewport_con[3] = std::bit_cast<u32>(input_image_height);
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width); viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height); viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
viewport_con[6] = std::bit_cast<u32>(sharpening); viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
UploadImages(device, scheduler); UploadImages(device, scheduler);
UpdateDescriptorSets(device, source_image_view, image_index); UpdateDescriptorSets(device, source_image_view, image_index);
@@ -491,7 +491,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.subpass = 0, .subpass = 0,
.basePipelineHandle = 0, .basePipelineHandle = 0,
.basePipelineIndex = 0, .basePipelineIndex = 0,
}); }, device.StaticPipelineCache());
} }
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
@@ -22,7 +22,9 @@
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" #include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -268,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor
.layout = *layout, .layout = *layout,
.basePipelineHandle = {}, .basePipelineHandle = {},
.basePipelineIndex = 0, .basePipelineIndex = 0,
}); }, device.StaticPipelineCache());
} }
ComputePass::~ComputePass() = default; ComputePass::~ComputePass() = default;
@@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
}); });
} }
namespace {
constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
std::array<u32, 3> dim;
u32 bytes_per_block_log2;
std::array<u32, 3> origin;
u32 layer_stride;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
};
static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL2D_BINDINGS{{
{
.binding = BL2D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL2D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL2D_TEMPLATE{{
{
.dstBinding = BL2D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
}};
constexpr DescriptorBankInfo BL2D_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
};
} // Anonymous namespace
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
BL2D_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) {
if (info.type != VideoCommon::ImageType::e2D) {
return false;
}
if (info.resources.levels != 1 || info.resources.layers != 1) {
return false;
}
if (info.num_samples > 1) {
return false;
}
if (VideoCore::Surface::IsPixelFormatASTC(info.format) ||
VideoCore::Surface::IsPixelFormatBCn(info.format)) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
return false;
}
return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 &&
VideoCore::Surface::DefaultBlockHeight(info.format) == 1;
}
void BlockLinearUnswizzle2DPass::Unswizzle(
Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
if (swizzles.empty()) {
return;
}
const VideoCommon::SwizzleParameters& sw = swizzles.front();
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
const u32 width = sw.num_tiles.width;
const u32 height = sw.num_tiles.height;
const u32 depth = image.info.resources.layers;
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
const VkDeviceSize output_size =
static_cast<VkDeviceSize>(width) * height * depth * bytes_per_block;
const StagingBufferRef output =
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
BlockLinearUnswizzle2DPushConstants pc{};
pc.dim = {width, height, depth};
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
pc.origin = params.origin;
pc.layer_stride = params.layer_stride;
pc.block_size = params.block_size;
pc.x_shift = params.x_shift;
pc.block_height = params.block_height;
pc.block_height_mask = params.block_height_mask;
scheduler.RequestOutsideRenderPassOperationContext();
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
const VkDescriptorSet set = descriptor_allocator.Commit();
const u32 gx = Common::DivCeil(width, 16u);
const u32 gy = Common::DivCeil(height, 8u);
const bool is_initialized = image.ExchangeInitialization();
const VkBuffer out_buffer = output.buffer;
const VkDeviceSize out_offset = output.offset;
const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask();
scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
out_offset, dst_image, aspect, width, height,
is_initialized](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return;
}
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
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, 0, sizeof(pc), &pc);
cmdbuf.Dispatch(gx, gy, depth);
const VkBufferMemoryBarrier buffer_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = out_offset,
.size = output_size,
};
const VkImageMemoryBarrier pre_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
const VkBufferImageCopy copy{
.bufferOffset = out_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = depth,
},
.imageOffset = {0, 0, 0},
.imageExtent = {width, height, 1},
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
const VkImageMemoryBarrier post_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_copy);
});
}
namespace {
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
std::array<u32, 3> dim;
u32 bytes_per_block_log2;
std::array<u32, 3> origin;
u32 slice_size;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 block_depth;
u32 block_depth_mask;
};
static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL3DB_BINDINGS{{
{
.binding = BL3DB_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3DB_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL3DB_TEMPLATE{{
{
.dstBinding = BL3DB_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
}};
constexpr DescriptorBankInfo BL3DB_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
};
} // Anonymous namespace
BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
BL3DB_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DBufferPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
const VideoCommon::ImageInfo& info) {
if (info.type != VideoCommon::ImageType::e3D) {
return false;
}
if (info.resources.levels != 1 || info.resources.layers != 1) {
return false;
}
if (info.num_samples > 1) {
return false;
}
if (info.size.depth <= 1) {
return false;
}
if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
return false;
}
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
}
void BlockLinearUnswizzle3DBufferPass::Unswizzle(
Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
if (swizzles.empty()) {
return;
}
const VideoCommon::SwizzleParameters& sw = swizzles.front();
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
const u32 blocks_x = sw.num_tiles.width;
const u32 blocks_y = sw.num_tiles.height;
const u32 blocks_z = sw.num_tiles.depth;
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
const VkDeviceSize output_size =
static_cast<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block;
const StagingBufferRef output =
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
BlockLinearUnswizzle3DBufferPushConstants pc{};
pc.dim = {blocks_x, blocks_y, blocks_z};
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
pc.origin = params.origin;
pc.slice_size = params.slice_size;
pc.block_size = params.block_size;
pc.x_shift = params.x_shift;
pc.block_height = params.block_height;
pc.block_height_mask = params.block_height_mask;
pc.block_depth = params.block_depth;
pc.block_depth_mask = params.block_depth_mask;
scheduler.RequestOutsideRenderPassOperationContext();
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
const VkDescriptorSet set = descriptor_allocator.Commit();
const u32 gx = Common::DivCeil(blocks_x, 8u);
const u32 gy = Common::DivCeil(blocks_y, 8u);
const u32 gz = Common::DivCeil(blocks_z, 4u);
const bool is_initialized = image.ExchangeInitialization();
const VkBuffer out_buffer = output.buffer;
const VkDeviceSize out_offset = output.offset;
const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask();
const VkExtent3D extent{
.width = image.info.size.width,
.height = image.info.size.height,
.depth = image.info.size.depth,
};
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
out_offset, dst_image, aspect, extent,
is_initialized](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return;
}
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
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, 0, sizeof(pc), &pc);
cmdbuf.Dispatch(gx, gy, gz);
const VkBufferMemoryBarrier buffer_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = out_offset,
.size = output_size,
};
const VkImageMemoryBarrier pre_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
const VkBufferImageCopy copy{
.bufferOffset = out_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {0, 0, 0},
.imageExtent = extent,
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
const VkImageMemoryBarrier post_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_copy);
});
}
} // namespace Vulkan } // namespace Vulkan
@@ -164,4 +164,42 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue; ComputePassDescriptorQueue& compute_pass_descriptor_queue;
}; };
class BlockLinearUnswizzle2DPass final : public ComputePass {
public:
explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle2DPass();
[[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info);
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
public:
explicit BlockLinearUnswizzle3DBufferPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle3DBufferPass();
[[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
} // namespace Vulkan } // namespace Vulkan
@@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d
}); });
key.resolve_color = key.resolve_color =
key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler(); key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler();
key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() &&
SupportsDepthStencilResolve(device, key.depth_format);
return key; return key;
} }
@@ -63,6 +63,8 @@ using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment; using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 18; constexpr u32 CACHE_VERSION = 18;
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container> template <typename Container>
@@ -150,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde
return Shader::AttributeType::Disabled; return Shader::AttributeType::Disabled;
} }
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return Shader::InputTopology::Points;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
return Shader::InputTopology::Lines;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return Shader::InputTopology::LinesAdjacency;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return Shader::InputTopology::TrianglesAdjacency;
default:
return Shader::InputTopology::Triangles;
}
}
Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> programs, Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> programs,
const GraphicsPipelineCacheKey& key, const GraphicsPipelineCacheKey& key,
const Shader::IR::Program& program, const Shader::IR::Program& program,
@@ -268,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
default: default:
break; break;
} }
switch (key.state.topology) { info.input_topology = MaxwellToInputTopology(key.state.topology);
case Maxwell::PrimitiveTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
case Maxwell::PrimitiveTopology::Patches:
info.input_topology = Shader::InputTopology::Triangles;
break;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.force_early_z = key.state.early_z != 0; info.force_early_z = key.state.early_z != 0;
info.y_negate = key.state.y_negate != 0; info.y_negate = key.state.y_negate != 0;
return info; return info;
@@ -699,6 +694,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (use_vulkan_pipeline_cache) { if (use_vulkan_pipeline_cache) {
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION); CACHE_VERSION);
size_t size = 0;
vulkan_pipeline_cache.Read(&size, nullptr);
last_cache_size.store(size, std::memory_order_relaxed);
last_flush = std::chrono::steady_clock::now();
} }
if (state.statistics) { if (state.statistics) {
@@ -706,6 +705,35 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
} }
} }
void PipelineCache::QueueVulkanPipelineCacheFlush() {
if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) {
return;
}
if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) {
return;
}
const auto now = std::chrono::steady_clock::now();
const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024);
const std::chrono::seconds interval{
std::max<size_t>(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)};
if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) {
return;
}
if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) {
return;
}
pipelines_since_flush = 0;
last_flush = now;
serialization_thread.QueueWork([this] {
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
size_t size = 0;
vulkan_pipeline_cache.Read(&size, nullptr);
last_cache_size.store(size, std::memory_order_relaxed);
flush_in_flight.store(false, std::memory_order_release);
});
}
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
auto& pipeline{pair->second}; auto& pipeline{pair->second};
@@ -744,7 +772,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics, std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
bool build_in_parallel) try { bool build_in_parallel) try {
auto hash = key.Hash(); auto hash = key.Hash();
LOG_INFO(Render_Vulkan, "{:#016x}", hash); LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
size_t env_index{0}; size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_a{key.unique_hashes[0] != 0};
@@ -758,8 +786,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
index == static_cast<u32>(Maxwell::ShaderType::Geometry); index == static_cast<u32>(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) { if (key.unique_hashes[index] == 0 && is_emulated_stage) {
auto topology = MaxwellToOutputTopology(key.state.topology); auto topology = MaxwellToOutputTopology(key.state.topology);
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, programs[index] =
*layer_source_program, topology); GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology,
MaxwellToInputTopology(key.state.topology));
continue; continue;
} }
if (key.unique_hashes[index] == 0) { if (key.unique_hashes[index] == 0) {
@@ -772,11 +802,13 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (!uses_vertex_a || index != 1) { if (!uses_vertex_a || index != 1) {
// Normal path // Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.state.topology));
} else { } else {
// VertexB path when VertexA is present. // VertexB path when VertexA is present.
auto& program_va{programs[0]}; auto& program_va{programs[0]};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.state.topology))};
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
} }
@@ -880,6 +912,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
} }
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION); SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
}); });
QueueVulkanPipelineCacheFlush();
return pipeline; return pipeline;
} }
@@ -899,6 +932,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_}, SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
pipeline_cache_filename, CACHE_VERSION); pipeline_cache_filename, CACHE_VERSION);
}); });
QueueVulkanPipelineCacheFlush();
return pipeline; return pipeline;
} }
@@ -911,7 +945,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
return nullptr; return nullptr;
} }
LOG_INFO(Render_Vulkan, "{:#016x}", hash); LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -920,7 +954,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash); env.Dump(hash, key.unique_hash);
} }
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
Shader::InputTopology::Points)};
const VkDriverIdKHR driver_id = device.GetDriverID(); const VkDriverIdKHR driver_id = device.GetDriverID();
const bool needs_shared_mem_clamp = const bool needs_shared_mem_clamp =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
@@ -7,6 +7,8 @@
#pragma once #pragma once
#include <array> #include <array>
#include <atomic>
#include <chrono>
#include <cstddef> #include <cstddef>
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
@@ -144,6 +146,8 @@ private:
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
u32 expected_cache_version); u32 expected_cache_version);
void QueueVulkanPipelineCacheFlush();
const Device& device; const Device& device;
Scheduler& scheduler; Scheduler& scheduler;
DescriptorPool& descriptor_pool; DescriptorPool& descriptor_pool;
@@ -171,6 +175,10 @@ private:
std::filesystem::path vulkan_pipeline_cache_filename; std::filesystem::path vulkan_pipeline_cache_filename;
vk::PipelineCache vulkan_pipeline_cache; vk::PipelineCache vulkan_pipeline_cache;
size_t pipelines_since_flush{};
std::chrono::steady_clock::time_point last_flush{};
std::atomic<size_t> last_cache_size{};
std::atomic_bool flush_in_flight{};
Common::ThreadWorker workers; Common::ThreadWorker workers;
Common::ThreadWorker serialization_thread; Common::ThreadWorker serialization_thread;
@@ -725,6 +725,9 @@ public:
if (!device.IsExtTransformFeedbackSupported()) { if (!device.IsExtTransformFeedbackSupported()) {
return; return;
} }
if (!scheduler.IsRenderPassActive()) {
return;
}
FlushBeginTFB(); FlushBeginTFB();
has_started = true; has_started = true;
} }
@@ -741,6 +744,9 @@ public:
if (has_flushed_end_pending) { if (has_flushed_end_pending) {
if (scheduler.IsRenderPassActive()) { if (scheduler.IsRenderPassActive()) {
FlushEndTFB(); FlushEndTFB();
} else {
has_flushed_end_pending = false;
has_started = false;
} }
} }
runtime.View3DRegs([this](Maxwell3D& maxwell3d) { runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
@@ -1286,33 +1286,42 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM; regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
const bool forces_unorm_representation = device.IsExtDepthBiasControlSupported();
if (is_d24 && !device.SupportsD24DepthBuffer()) { if (is_d24 && !device.SupportsD24DepthBuffer()) {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]); static constexpr double GUEST_TO_HOST_UNORM_BITS =
static_cast<double>(1ULL << (32 - 24));
static constexpr const u64* start = NEEDS_D24; if (forces_unorm_representation) {
static constexpr const u64* end = NEEDS_D24 + length; units = static_cast<float>(static_cast<double>(units) * GUEST_TO_HOST_UNORM_BITS);
} else {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
const u64* it = std::find(start, end, program_id); static constexpr const u64* start = NEEDS_D24;
static constexpr const u64* end = NEEDS_D24 + length;
if (it != end) { const u64* it = std::find(start, end, program_id);
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias if (it != end) {
const double rescale_factor = // the base formulas can be obtained from here:
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127)); // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
units = static_cast<float>(static_cast<double>(units) * rescale_factor); const double rescale_factor =
GUEST_TO_HOST_UNORM_BITS / (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
}
} }
} }
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) { factor = regs.slope_scale_depth_bias,
if (device.IsExtDepthBiasControlSupported()) { forces_unorm_representation, this](vk::CommandBuffer cmdbuf) {
static VkDepthBiasRepresentationInfoEXT bias_info{ if (forces_unorm_representation) {
const VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT, .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr, .pNext = nullptr,
.depthBiasRepresentation = .depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT, VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = VK_FALSE, .depthBiasExact = static_cast<VkBool32>(device.HasExactDepthBiasControl()),
}; };
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info); cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
@@ -1336,8 +1345,14 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs)
if (!state_tracker.TouchDepthBounds()) { if (!state_tracker.TouchDepthBounds()) {
return; return;
} }
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]]( float min = regs.depth_bounds[0];
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); }); float max = regs.depth_bounds[1];
if (!device.IsExtDepthRangeUnrestrictedSupported()) {
min = std::clamp(min, 0.0f, 1.0f);
max = std::clamp(max, 0.0f, 1.0f);
}
scheduler.Record(
[min, max](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
} }
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -65,8 +65,61 @@ using VideoCore::Surface::SurfaceType;
.finalLayout = VK_IMAGE_LAYOUT_GENERAL, .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
}; };
} }
struct ResolveAspects {
bool depth;
bool stencil;
};
struct ResolveModes {
VkResolveModeFlagBits depth;
VkResolveModeFlagBits stencil;
};
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
const SurfaceType surface_type = GetSurfaceType(format);
return ResolveAspects{
.depth = surface_type == SurfaceType::Depth ||
surface_type == SurfaceType::DepthStencil,
.stencil = surface_type == SurfaceType::Stencil ||
surface_type == SurfaceType::DepthStencil,
};
}
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
const ResolveAspects aspects = GetResolveAspects(format);
ResolveModes modes{
.depth = VK_RESOLVE_MODE_NONE,
.stencil = VK_RESOLVE_MODE_NONE,
};
if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) {
modes.depth = mode;
}
if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) {
modes.stencil = mode;
}
return modes;
}
} // Anonymous namespace } // Anonymous namespace
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
return false;
}
const ResolveAspects aspects = GetResolveAspects(depth_format);
if (!aspects.depth && !aspects.stencil) {
return false;
}
const ResolveModes modes = PickResolveModes(device, depth_format);
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
return false;
}
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
}
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
@@ -75,7 +128,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
if (!is_new) { if (!is_new) {
return *pair->second; return *pair->second;
} }
boost::container::static_vector<VkAttachmentDescription, 9> descriptions; static constexpr size_t MAX_ATTACHMENTS =
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
std::array<VkAttachmentReference, 8> references{}; std::array<VkAttachmentReference, 8> references{};
u32 num_attachments{}; u32 num_attachments{};
u32 num_colors{}; u32 num_colors{};
@@ -133,6 +188,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
} }
} }
} }
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
key.samples != VK_SAMPLE_COUNT_1_BIT &&
SupportsDepthStencilResolve(*device, key.depth_format);
VkAttachmentReference depth_resolve_reference{};
if (do_resolve_depth_stencil) {
depth_resolve_reference = VkAttachmentReference{
.attachment = static_cast<u32>(descriptions.size()),
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
VkAttachmentDescription resolve_desc =
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
descriptions.push_back(resolve_desc);
}
const VkSubpassDescription subpass{ const VkSubpassDescription subpass{
.flags = 0, .flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -145,18 +215,117 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.preserveAttachmentCount = 0, .preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr, .pPreserveAttachments = nullptr,
}; };
const VkSubpassDependency dependency{ const VkSubpassDependency counter_resume_dependency{
.srcSubpass = 0, // Current subpass .srcSubpass = 0,
.dstSubpass = 0, // Same subpass (self-dependency) .dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | .srcStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | .dstStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | .dependencyFlags = 0,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
}; };
const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported();
if (device->IsKhrCreateRenderPass2Supported()) {
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
for (const VkAttachmentDescription& description : descriptions) {
descriptions2.push_back(VkAttachmentDescription2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
.pNext = nullptr,
.flags = description.flags,
.format = description.format,
.samples = description.samples,
.loadOp = description.loadOp,
.storeOp = description.storeOp,
.stencilLoadOp = description.stencilLoadOp,
.stencilStoreOp = description.stencilStoreOp,
.initialLayout = description.initialLayout,
.finalLayout = description.finalLayout,
});
}
const auto promote = [](const VkAttachmentReference& reference) {
return VkAttachmentReference2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.pNext = nullptr,
.attachment = reference.attachment,
.layout = reference.layout,
.aspectMask = 0,
};
};
std::array<VkAttachmentReference2, 8> references2{};
std::array<VkAttachmentReference2, 8> resolve_references2{};
for (size_t index = 0; index < references.size(); ++index) {
references2[index] = promote(references[index]);
resolve_references2[index] = promote(resolve_references[index]);
}
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
.pNext = nullptr,
.depthResolveMode = resolve_modes.depth,
.stencilResolveMode = resolve_modes.stencil,
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
};
const VkSubpassDescription2 subpass2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.viewMask = 0,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = num_attachments,
.pColorAttachments = references2.data(),
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
const VkMemoryBarrier2 counter_resume_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
.srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
.dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
};
VkSubpassDependency2 counter_resume_dependency2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
.pNext = nullptr,
.srcSubpass = counter_resume_dependency.srcSubpass,
.dstSubpass = counter_resume_dependency.dstSubpass,
.srcStageMask = counter_resume_dependency.srcStageMask,
.dstStageMask = counter_resume_dependency.dstStageMask,
.srcAccessMask = counter_resume_dependency.srcAccessMask,
.dstAccessMask = counter_resume_dependency.dstAccessMask,
.dependencyFlags = counter_resume_dependency.dependencyFlags,
.viewOffset = 0,
};
if (device->HasSynchronization2()) {
counter_resume_dependency2.pNext = &counter_resume_barrier;
counter_resume_dependency2.srcStageMask = 0;
counter_resume_dependency2.dstStageMask = 0;
counter_resume_dependency2.srcAccessMask = 0;
counter_resume_dependency2.dstAccessMask = 0;
}
pair->second = device->GetLogical().CreateRenderPass2({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
.pNext = nullptr,
.flags = 0,
.attachmentCount = static_cast<u32>(descriptions2.size()),
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
.subpassCount = 1,
.pSubpasses = &subpass2,
.dependencyCount = can_resume_transform_feedback ? 1u : 0u,
.pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr,
.correlatedViewMaskCount = 0,
.pCorrelatedViewMasks = nullptr,
});
return *pair->second;
}
pair->second = device->GetLogical().CreateRenderPass({ pair->second = device->GetLogical().CreateRenderPass({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@@ -165,8 +334,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(), .pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
.subpassCount = 1, .subpassCount = 1,
.pSubpasses = &subpass, .pSubpasses = &subpass,
.dependencyCount = 1, .dependencyCount = can_resume_transform_feedback ? 1u : 0u,
.pDependencies = &dependency, .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency : nullptr,
}); });
return *pair->second; return *pair->second;
} }
@@ -9,6 +9,7 @@
#include <mutex> #include <mutex>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include "common/container_hash.h"
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -21,6 +22,7 @@ struct RenderPassKey {
VideoCore::Surface::PixelFormat depth_format; VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples; VkSampleCountFlagBits samples;
bool resolve_color; bool resolve_color;
bool resolve_depth_stencil;
u32 color_clear_mask; u32 color_clear_mask;
bool depth_stencil_clear; bool depth_stencil_clear;
u32 color_discard_mask; u32 color_discard_mask;
@@ -31,17 +33,27 @@ struct RenderPassKey {
namespace std { namespace std {
template <> template <>
struct hash<Vulkan::RenderPassKey> { struct hash<Vulkan::RenderPassKey> {
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept { [[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
size_t value = static_cast<size_t>(key.depth_format) << 48; u64 formats = 0;
value ^= static_cast<size_t>(key.samples) << 52; for (size_t index = 0; index < key.color_formats.size(); ++index) {
value ^= static_cast<size_t>(key.resolve_color) << 63; formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
for (size_t i = 0; i < key.color_formats.size(); ++i) {
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
} }
return value; const u64 state = static_cast<u64>(key.depth_format) |
(static_cast<u64>(key.samples) << 8) |
(static_cast<u64>(key.color_clear_mask) << 16) |
(static_cast<u64>(key.color_discard_mask) << 24) |
(static_cast<u64>(key.resolve_color) << 32) |
(static_cast<u64>(key.depth_stencil_clear) << 33) |
(static_cast<u64>(key.resolve_depth_stencil) << 34);
size_t seed = 0;
Common::HashCombine(seed, formats);
Common::HashCombine(seed, state);
return seed;
} }
}; };
} // namespace std } // namespace std
@@ -50,6 +62,9 @@ namespace Vulkan {
class Device; class Device;
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
VideoCore::Surface::PixelFormat depth_format);
class RenderPassCache { class RenderPassCache {
public: public:
explicit RenderPassCache(const Device& device_); explicit RenderPassCache(const Device& device_);
@@ -410,8 +410,17 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images, Record([num_images = num_renderpass_images,
images = renderpass_images, images = renderpass_images,
ranges = renderpass_image_ranges, ranges = renderpass_image_ranges,
consumer_stages = device.AttachmentConsumerStages(),
has_transform_feedback = device.IsExtTransformFeedbackSupported()]( has_transform_feedback = device.IsExtTransformFeedbackSupported()](
vk::CommandBuffer cmdbuf) { vk::CommandBuffer cmdbuf) {
static constexpr VkAccessFlags SHADER_ACCESS =
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
static constexpr VkAccessFlags COLOR_ACCESS =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
std::array<VkImageMemoryBarrier, 9> barriers; std::array<VkImageMemoryBarrier, 9> barriers;
for (size_t i = 0; i < num_images; ++i) { for (size_t i = 0; i < num_images; ++i) {
const VkImageSubresourceRange& range = ranges[i]; const VkImageSubresourceRange& range = ranges[i];
@@ -421,24 +430,25 @@ void Scheduler::EndRenderPass()
| VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0;
VkAccessFlags src_access = 0; VkAccessFlags src_access = 0;
VkAccessFlags dst_access = SHADER_ACCESS;
if (is_color) if (is_color) {
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
else if (is_depth_stencil) dst_access |= COLOR_ACCESS;
} else if (is_depth_stencil) {
src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
else dst_access |= DEPTH_STENCIL_ACCESS;
} else {
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS;
}
barriers[i] = VkImageMemoryBarrier{ barriers[i] = VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr, .pNext = nullptr,
.srcAccessMask = src_access, .srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT .dstAccessMask = dst_access,
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL, .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
@@ -449,7 +459,7 @@ void Scheduler::EndRenderPass()
} }
cmdbuf.EndRenderPass(); cmdbuf.EndRenderPass();
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages,
0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); 0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
if (has_transform_feedback) { if (has_transform_feedback) {
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
@@ -628,18 +628,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
.subresourceRange = subresource_range, .subresourceRange = subresource_range,
}; };
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
read_barrier); read_barrier);
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies); cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
// TODO: Move this to another API // TODO: Move this to another API
cmdbuf.PipelineBarrier( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, nullptr, nullptr, write_barrier);
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, nullptr, nullptr, write_barrier);
} }
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region, [[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
@@ -933,6 +927,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue); staging_buffer_pool, compute_pass_descriptor_queue);
} }
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
} }
void TextureCacheRuntime::Finish() { void TextureCacheRuntime::Finish() {
@@ -995,13 +993,25 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
} }
VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers) { VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask) {
ResolveShadow& shadow = resolve_shadows[msaa_image]; ResolveShadow& shadow = resolve_shadows[msaa_image];
if (shadow.image && shadow.format == format && shadow.extent.width == extent.width && if (shadow.image && shadow.format == format && shadow.extent.width == extent.width &&
shadow.extent.height == extent.height && shadow.layers == layers) { shadow.extent.height == extent.height && shadow.layers == layers &&
shadow.aspect_mask == aspect_mask) {
shadow.up_to_date = true; shadow.up_to_date = true;
return *shadow.view; return *shadow.view;
} }
VkImageUsageFlags shadow_usage =
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
} else {
shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (shadow.image) {
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow));
}
shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{ shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@@ -1013,8 +1023,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
.arrayLayers = layers, .arrayLayers = layers,
.samples = VK_SAMPLE_COUNT_1_BIT, .samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL, .tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | .usage = shadow_usage,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@@ -1029,7 +1038,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
.format = format, .format = format,
.components{}, .components{},
.subresourceRange{ .subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = aspect_mask,
.baseMipLevel = 0, .baseMipLevel = 0,
.levelCount = 1, .levelCount = 1,
.baseArrayLayer = 0, .baseArrayLayer = 0,
@@ -1039,6 +1048,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
shadow.format = format; shadow.format = format;
shadow.extent = extent; shadow.extent = extent;
shadow.layers = layers; shadow.layers = layers;
shadow.aspect_mask = aspect_mask;
shadow.up_to_date = true; shadow.up_to_date = true;
return *shadow.view; return *shadow.view;
} }
@@ -1060,7 +1070,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) {
} }
void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) { void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) {
resolve_shadows.erase(msaa_image); const auto it = resolve_shadows.find(msaa_image);
if (it == resolve_shadows.end()) {
return;
}
if (it->second.image) {
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second));
}
resolve_shadows.erase(it);
} }
void TextureCacheRuntime::BarrierFeedbackLoop() { void TextureCacheRuntime::BarrierFeedbackLoop() {
@@ -1195,7 +1212,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, READ_BARRIER, {}, middle_out_barrier); 0, READ_BARRIER, {}, middle_out_barrier);
cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
vk_out_copies);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_barriers); 0, {}, {}, post_barriers);
}); });
@@ -1357,133 +1375,83 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
} }
switch (dst_view.format) { switch (dst_view.format) {
case PixelFormat::D24_UNORM_S8_UINT: case PixelFormat::R16_UNORM:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM if (src_view.format == PixelFormat::D16_UNORM) {
|| src_view.format == PixelFormat::B8G8R8A8_UNORM return blit_image_helper.ConvertD16ToR16(dst, src_view);
|| src_view.format == PixelFormat::A8B8G8R8_SRGB }
|| src_view.format == PixelFormat::B8G8R8A8_SRGB) { break;
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); case PixelFormat::A8B8G8R8_SRGB:
case PixelFormat::B8G8R8A8_SRGB:
case PixelFormat::B8G8R8A8_UNORM:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
} }
break; break;
case PixelFormat::A8B8G8R8_UNORM: case PixelFormat::A8B8G8R8_UNORM:
case PixelFormat::A8B8G8R8_SNORM: if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
case PixelFormat::A8B8G8R8_SINT: return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
case PixelFormat::A8B8G8R8_UINT: }
case PixelFormat::R5G6B5_UNORM: if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
case PixelFormat::B5G6R5_UNORM: return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
case PixelFormat::A1R5G5B5_UNORM: }
case PixelFormat::A2B10G10R10_UNORM: if (src_view.format == PixelFormat::D32_FLOAT) {
case PixelFormat::A2B10G10R10_UINT: return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
case PixelFormat::A2R10G10B10_UNORM: }
case PixelFormat::A1B5G5R5_UNORM: break;
case PixelFormat::A5B5G5R1_UNORM: case PixelFormat::R32_FLOAT:
case PixelFormat::R8_UNORM: if (src_view.format == PixelFormat::D32_FLOAT) {
case PixelFormat::R8_SNORM: return blit_image_helper.ConvertD32ToR32(dst, src_view);
case PixelFormat::R8_SINT:
case PixelFormat::R8_UINT:
case PixelFormat::R16G16B16A16_FLOAT:
case PixelFormat::R16G16B16A16_UNORM:
case PixelFormat::R16G16B16A16_SNORM:
case PixelFormat::R16G16B16A16_SINT:
case PixelFormat::R16G16B16A16_UINT:
case PixelFormat::B10G11R11_FLOAT:
case PixelFormat::R32G32B32A32_UINT:
case PixelFormat::BC1_RGBA_UNORM:
case PixelFormat::BC2_UNORM:
case PixelFormat::BC3_UNORM:
case PixelFormat::BC4_UNORM:
case PixelFormat::BC4_SNORM:
case PixelFormat::BC5_UNORM:
case PixelFormat::BC5_SNORM:
case PixelFormat::BC7_UNORM:
case PixelFormat::BC6H_UFLOAT:
case PixelFormat::BC6H_SFLOAT:
case PixelFormat::ASTC_2D_4X4_UNORM:
case PixelFormat::B8G8R8A8_UNORM:
case PixelFormat::R32G32B32A32_FLOAT:
case PixelFormat::R32G32B32A32_SINT:
case PixelFormat::R32G32_FLOAT:
case PixelFormat::R32G32_SINT:
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT &&
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
Settings::values.fix_bloom_effects.GetValue())) {
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(dst->RenderArea().width),
static_cast<s32>(dst->RenderArea().height)},
};
return blit_image_helper.BlitColor(dst, src_view, region, region,
Tegra::Engines::Fermi2D::Filter::Point,
Tegra::Engines::Fermi2D::Operation::SrcCopy);
} }
break; break;
case PixelFormat::R16_FLOAT:
case PixelFormat::R16_UNORM:
case PixelFormat::R16_SNORM:
case PixelFormat::R16_UINT:
case PixelFormat::R16_SINT:
case PixelFormat::R16G16_UNORM:
case PixelFormat::R16G16_FLOAT:
case PixelFormat::R16G16_UINT:
case PixelFormat::R16G16_SINT:
case PixelFormat::R16G16_SNORM:
case PixelFormat::R32G32B32_FLOAT:
case PixelFormat::A8B8G8R8_SRGB:
case PixelFormat::R8G8_UNORM:
case PixelFormat::R8G8_SNORM:
case PixelFormat::R8G8_SINT:
case PixelFormat::R8G8_UINT:
case PixelFormat::R32G32_UINT:
case PixelFormat::R16G16B16X16_FLOAT:
case PixelFormat::R32_UINT:
case PixelFormat::R32_SINT:
case PixelFormat::ASTC_2D_8X8_UNORM:
case PixelFormat::ASTC_2D_8X5_UNORM:
case PixelFormat::ASTC_2D_5X4_UNORM:
case PixelFormat::B8G8R8A8_SRGB:
case PixelFormat::BC1_RGBA_SRGB:
case PixelFormat::BC2_SRGB:
case PixelFormat::BC3_SRGB:
case PixelFormat::BC7_SRGB:
case PixelFormat::A4B4G4R4_UNORM:
case PixelFormat::G4R4_UNORM:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_UNORM:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X8_UNORM:
case PixelFormat::ASTC_2D_10X8_SRGB:
case PixelFormat::ASTC_2D_6X6_UNORM:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X6_UNORM:
case PixelFormat::ASTC_2D_10X6_SRGB:
case PixelFormat::ASTC_2D_10X5_UNORM:
case PixelFormat::ASTC_2D_10X5_SRGB:
case PixelFormat::ASTC_2D_10X10_UNORM:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X10_UNORM:
case PixelFormat::ASTC_2D_12X10_SRGB:
case PixelFormat::ASTC_2D_12X12_UNORM:
case PixelFormat::ASTC_2D_12X12_SRGB:
case PixelFormat::ASTC_2D_8X6_UNORM:
case PixelFormat::ASTC_2D_8X6_SRGB:
case PixelFormat::ASTC_2D_6X5_UNORM:
case PixelFormat::ASTC_2D_6X5_SRGB:
case PixelFormat::E5B9G9R9_FLOAT:
case PixelFormat::D32_FLOAT:
case PixelFormat::D16_UNORM: case PixelFormat::D16_UNORM:
case PixelFormat::X8_D24_UNORM: if (src_view.format == PixelFormat::R16_UNORM) {
case PixelFormat::S8_UINT: return blit_image_helper.ConvertR16ToD16(dst, src_view);
}
break;
case PixelFormat::S8_UINT_D24_UNORM: case PixelFormat::S8_UINT_D24_UNORM:
case PixelFormat::D32_FLOAT_S8_UINT: if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
case PixelFormat::Invalid: src_view.format == PixelFormat::B8G8R8A8_UNORM) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
}
break;
case PixelFormat::D32_FLOAT:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
}
if (src_view.format == PixelFormat::R32_FLOAT) {
return blit_image_helper.ConvertR32ToD32(dst, src_view);
}
break;
case PixelFormat::D24_UNORM_S8_UINT:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
}
break;
default: default:
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
break; break;
} }
if (src_view.format == PixelFormat::D32_FLOAT &&
VideoCore::Surface::GetFormatType(dst_view.format) == SurfaceType::ColorTexture &&
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
Settings::values.fix_bloom_effects.GetValue())) {
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(dst->RenderArea().width),
static_cast<s32>(dst->RenderArea().height)},
};
return blit_image_helper.BlitColor(dst, src_view, region, region,
Tegra::Engines::Fermi2D::Filter::Point,
Tegra::Engines::Fermi2D::Operation::SrcCopy);
}
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
} }
VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format, VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format,
@@ -1640,31 +1608,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) { std::span<const VideoCommon::ImageCopy> copies) {
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1; const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples; const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples;
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
return;
}
if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 && if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 &&
src.info.format == dst.info.format) { src.info.format == dst.info.format) {
const VideoCommon::ImageCopy& copy = copies.front(); const VideoCommon::ImageCopy& copy = copies.front();
const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle()); const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle());
if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 && if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() &&
copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
copy.src_subresource.base_level == 0 && copy.src_subresource.base_level == 0 &&
static_cast<u32>(copy.extent.width) <= shadow->extent.width && static_cast<u32>(copy.extent.width) <= shadow->extent.width &&
static_cast<u32>(copy.extent.height) <= shadow->extent.height) { static_cast<u32>(copy.extent.height) <= shadow->extent.height &&
static_cast<u32>(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <=
shadow->layers) {
const VkImageAspectFlags aspect_mask = shadow->aspect_mask;
VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkAccessFlags attachment_read_write =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
const VkImage shadow_image = *shadow->image; const VkImage shadow_image = *shadow->image;
const VkImage dst_image = dst.Handle(); const VkImage dst_image = dst.Handle();
const VkImageCopy region{ const VkImageCopy region{
.srcSubresource{ .srcSubresource{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = aspect_mask,
.mipLevel = 0, .mipLevel = 0,
.baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer), .baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
.layerCount = static_cast<u32>(copy.src_subresource.num_layers), .layerCount = static_cast<u32>(copy.src_subresource.num_layers),
}, },
.srcOffset = {0, 0, 0}, .srcOffset = {0, 0, 0},
.dstSubresource{ .dstSubresource{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = aspect_mask,
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level), .mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer), .baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
.layerCount = static_cast<u32>(copy.dst_subresource.num_layers), .layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
@@ -1673,26 +1651,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.extent = {copy.extent.width, copy.extent.height, 1}, .extent = {copy.extent.width, copy.extent.height, 1},
}; };
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) { scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage,
attachment_write,
attachment_read_write](vk::CommandBuffer cmdbuf) {
const std::array pre_barriers{ const std::array pre_barriers{
VkImageMemoryBarrier{ VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr, .pNext = nullptr,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .srcAccessMask = attachment_write,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL, .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image, .image = shadow_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS}, VK_REMAINING_ARRAY_LAYERS},
}, },
VkImageMemoryBarrier{ VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr, .pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL, .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
@@ -1700,7 +1679,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image, .image = dst_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS}, VK_REMAINING_ARRAY_LAYERS},
}, },
}; };
@@ -1715,28 +1694,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image, .image = shadow_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS}, VK_REMAINING_ARRAY_LAYERS},
}, },
VkImageMemoryBarrier{ VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr, .pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image, .image = dst_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS}, VK_REMAINING_ARRAY_LAYERS},
}, },
}; };
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr,
pre_barriers); pre_barriers);
cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
@@ -1748,6 +1724,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
return; return;
} }
} }
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
return;
}
blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(), blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(),
src.info.format, num_samples, copies, msaa_to_non_msaa); src.info.format, num_samples, copies, msaa_to_non_msaa);
} }
@@ -1772,6 +1753,9 @@ void TextureCacheRuntime::TickFrame() {
std::erase_if(pending_msaa_images, [this](const auto& pending) { std::erase_if(pending_msaa_images, [this](const auto& pending) {
return scheduler.IsFree(pending.first); return scheduler.IsFree(pending.first);
}); });
std::erase_if(pending_resolve_shadows, [this](const auto& pending) {
return scheduler.IsFree(pending.first);
});
} }
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1796,6 +1780,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
} }
flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad; flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} else if (runtime->bl3db_unswizzle_pass &&
BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} }
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) { if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::Converted;
@@ -1882,11 +1873,21 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
ScaleDown(true); ScaleDown(true);
} }
const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
const bool wants_msaa_upload = info.num_samples > 1 const bool wants_msaa_upload = info.num_samples > 1
&& (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth)
&& !VideoCore::Surface::IsPixelFormatInteger(info.format); && !VideoCore::Surface::IsPixelFormatInteger(info.format);
if (wants_msaa_upload) { if (wants_msaa_upload) {
const bool msaa_upload_copies_stencil =
msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 &&
runtime->device.IsExtShaderStencilExportSupported();
const VkImageAspectFlags upload_aspect_mask =
msaa_upload_is_depth
? (msaa_upload_copies_stencil
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: VK_IMAGE_ASPECT_DEPTH_BIT)
: aspect_mask;
ImageInfo temp_info = info; ImageInfo temp_info = info;
temp_info.num_samples = 1; temp_info.num_samples = 1;
@@ -1898,10 +1899,10 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
scheduler->RequestOutsideRenderPassOperationContext(); scheduler->RequestOutsideRenderPassOperationContext();
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask);
const VkBuffer src_buffer = buffer; const VkBuffer src_buffer = buffer;
const VkImage temp_vk_image = *temp_image; const VkImage temp_vk_image = *temp_image;
const VkImageAspectFlags vk_aspect_mask = aspect_mask; const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask;
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask,
vk_copies](vk::CommandBuffer cmdbuf) { vk_copies](vk::CommandBuffer cmdbuf) {
@@ -1923,9 +1924,16 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
image_copies.push_back(image_copy); image_copies.push_back(image_copy);
} }
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, if (msaa_upload_is_depth) {
temp_vk_image, info.format, info.num_samples, runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(),
image_copies, false); info.format, temp_vk_image, info.format,
info.num_samples, image_copies,
msaa_upload_copies_stencil);
} else {
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
temp_vk_image, info.format, info.num_samples,
image_copies, false);
}
initialized = true; initialized = true;
runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image)); runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image));
@@ -2690,7 +2698,7 @@ Framebuffer::~Framebuffer() = default;
void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
std::span<ImageView*, NUM_RT> color_buffers, std::span<ImageView*, NUM_RT> color_buffers,
ImageView* depth_buffer, bool is_rescaled_) { ImageView* depth_buffer, bool is_rescaled_) {
boost::container::small_vector<VkImageView, NUM_RT + 1> attachments; boost::container::small_vector<VkImageView, NUM_RT * 2 + 2> attachments;
RenderPassKey renderpass_key{}; RenderPassKey renderpass_key{};
s32 num_layers = 1; s32 num_layers = 1;
@@ -2719,6 +2727,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
++num_images; ++num_images;
} }
const size_t num_colors = attachments.size(); const size_t num_colors = attachments.size();
VkImage depth_image = VK_NULL_HANDLE;
VkImageAspectFlags depth_aspect_mask = 0;
if (depth_buffer) { if (depth_buffer) {
width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width)
: depth_buffer->size.width); : depth_buffer->size.width);
@@ -2734,6 +2744,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
++num_images; ++num_images;
has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
depth_image = depth_buffer->ImageHandle();
depth_aspect_mask = subresource_range.aspectMask;
} else { } else {
renderpass_key.depth_format = PixelFormat::Invalid; renderpass_key.depth_format = PixelFormat::Invalid;
} }
@@ -2742,6 +2754,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler();
renderpass_key.resolve_color = do_resolve_color; renderpass_key.resolve_color = do_resolve_color;
const bool do_resolve_depth_stencil =
samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE &&
runtime.device.IsTiler() &&
SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format);
renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil;
discard_msaa_color = discard_msaa_color =
ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
@@ -2762,8 +2780,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format; MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format;
if (ENABLE_MSAA_RESOLVE_CONSUME) { if (ENABLE_MSAA_RESOLVE_CONSUME) {
const VkImage msaa_image = images[rt_map[index]]; const VkImage msaa_image = images[rt_map[index]];
attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format, attachments.push_back(runtime.GetOrCreateResolveShadow(
render_area, layers)); msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT));
continue; continue;
} }
VkImageCreateInfo resolve_ci{ VkImageCreateInfo resolve_ci{
@@ -2808,6 +2826,16 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
} }
} }
if (do_resolve_depth_stencil) {
const u32 layers = static_cast<u32>((std::max)(num_layers, 1));
const VkFormat vk_format =
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true,
renderpass_key.depth_format)
.format;
attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area,
layers, depth_aspect_mask));
}
num_color_buffers = static_cast<u32>(num_colors); num_color_buffers = static_cast<u32>(num_colors);
framebuffer = runtime.device.GetLogical().CreateFramebuffer({ framebuffer = runtime.device.GetLogical().CreateFramebuffer({
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
@@ -2827,11 +2855,25 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) { if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
return renderpass; return renderpass;
} }
static_assert(NUM_RT <= 8);
const u32 variant_key = color_clear_mask | (color_discard_mask << 8) |
(static_cast<u32>(depth_stencil_clear) << 16);
for (u32 index = 0; index < num_memoized_variants; ++index) {
if (variant_keys[index] == variant_key) {
return variant_render_passes[index];
}
}
RenderPassKey key = render_pass_key; RenderPassKey key = render_pass_key;
key.color_clear_mask = color_clear_mask; key.color_clear_mask = color_clear_mask;
key.depth_stencil_clear = depth_stencil_clear; key.depth_stencil_clear = depth_stencil_clear;
key.color_discard_mask = color_discard_mask; key.color_discard_mask = color_discard_mask;
return render_pass_cache->Get(key); const VkRenderPass variant = render_pass_cache->Get(key);
if (num_memoized_variants < variant_keys.size()) {
variant_keys[num_memoized_variants] = variant_key;
variant_render_passes[num_memoized_variants] = variant;
++num_memoized_variants;
}
return variant;
} }
void TextureCacheRuntime::AccelerateImageUpload( void TextureCacheRuntime::AccelerateImageUpload(
@@ -2843,6 +2885,15 @@ void TextureCacheRuntime::AccelerateImageUpload(
return astc_decoder_pass->Assemble(image, map, swizzles); return astc_decoder_pass->Assemble(image, map, swizzles);
} }
if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) {
return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
}
if (bl3db_unswizzle_pass &&
BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
}
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
@@ -117,11 +117,13 @@ public:
VkFormat format = VK_FORMAT_UNDEFINED; VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent{}; VkExtent2D extent{};
u32 layers = 0; u32 layers = 0;
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
bool up_to_date = false; bool up_to_date = false;
}; };
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, [[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers); VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask);
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const; [[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
@@ -149,6 +151,8 @@ public:
std::optional<ASTCDecoderPass> astc_decoder_pass; std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass; std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
std::optional<BlockLinearUnswizzle3DBufferPass> bl3db_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution; const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats; std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
@@ -156,6 +160,7 @@ public:
std::array<vk::Buffer, indexing_slots> buffers{}; std::array<vk::Buffer, indexing_slots> buffers{};
std::vector<std::pair<u64, vk::Image>> pending_msaa_images; std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows; ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
}; };
class Framebuffer { class Framebuffer {
@@ -246,6 +251,8 @@ public:
} }
private: private:
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
vk::Framebuffer framebuffer; vk::Framebuffer framebuffer;
VkRenderPass renderpass{}; VkRenderPass renderpass{};
VkExtent2D render_area{}; VkExtent2D render_area{};
@@ -263,6 +270,9 @@ private:
RenderPassKey render_pass_key{}; RenderPassKey render_pass_key{};
RenderPassCache* render_pass_cache{nullptr}; RenderPassCache* render_pass_cache{nullptr};
bool discard_msaa_color{}; bool discard_msaa_color{};
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
mutable u32 num_memoized_variants{};
}; };
class Image : public VideoCommon::ImageBase { class Image : public VideoCommon::ImageBase {
+58 -35
View File
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array> #include <array>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
@@ -30,12 +31,62 @@ constexpr u32 pdep(u32 value) {
return result; return result;
} }
constexpr u32 SWIZZLE_RUN_BYTES = 16;
constexpr u32 SWIZZLE_RUN_SHIFT = 4;
constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1;
constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1;
static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK);
constexpr std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> SWIZZLE_X_RUN_TABLE = [] {
std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> table{};
for (u32 index = 0; index < static_cast<u32>(table.size()); ++index) {
table[index] = pdep<SWIZZLE_X_BITS>(index << SWIZZLE_RUN_SHIFT);
}
return table;
}();
template <u32 mask, u32 incr_amount> template <u32 mask, u32 incr_amount>
void incrpdep(u32& value) { void incrpdep(u32& value) {
static constexpr u32 swizzled_incr = pdep<mask>(incr_amount); static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
value = ((value | ~mask) + swizzled_incr) & mask; value = ((value | ~mask) + swizzled_incr) & mask;
} }
template <bool TO_LINEAR>
void SwizzleRow(std::span<u8> output, std::span<const u8> input, u32 offset_zy, u32 swizzled_y,
u32 x_shift, u32 x, u32 num_bytes, u32 linear) {
const auto copy = [&](u32 swizzled_x, u32 count) {
const u32 swizzled =
offset_zy + ((x >> GOB_SIZE_X_SHIFT) << x_shift) + (swizzled_x | swizzled_y);
u8* const dst = &output[TO_LINEAR ? swizzled : linear];
const u8* const src = &input[TO_LINEAR ? linear : swizzled];
std::memcpy(dst, src, count);
x += count;
linear += count;
};
u32 swizzled_run = SWIZZLE_X_RUN_TABLE[(x >> SWIZZLE_RUN_SHIFT) & SWIZZLE_RUN_INDEX_MASK];
u32 remaining = num_bytes;
const u32 head =
(std::min)(SWIZZLE_RUN_BYTES - (x & SWIZZLE_RUN_MASK), remaining) & SWIZZLE_RUN_MASK;
if (head != 0) {
copy(swizzled_run | (x & SWIZZLE_RUN_MASK), head);
remaining -= head;
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
}
while (remaining >= SWIZZLE_RUN_BYTES) {
copy(swizzled_run, SWIZZLE_RUN_BYTES);
remaining -= SWIZZLE_RUN_BYTES;
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
}
if (remaining != 0) {
copy(swizzled_run, remaining);
}
}
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL> template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth, void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth, u32 stride) { u32 block_height, u32 block_depth, u32 stride) {
@@ -70,23 +121,9 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
const u32 offset_y = (block_y >> block_height) * block_size + const u32 offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT); ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL); SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
for (u32 column = 0; column < width; origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL,
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { slice * pitch * height + line * pitch);
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
std::memcpy(dst, src, BYTES_PER_PIXEL);
}
} }
} }
} }
@@ -129,23 +166,9 @@ void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 wid
const u32 offset_y = (block_y >> block_height) * block_size + const u32 offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT); ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL); SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
for (u32 column = 0; column < extent_x; origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL,
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { slice * pitch * height + line * pitch);
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
std::memcpy(dst, src, BYTES_PER_PIXEL);
}
} }
unprocessed_lines -= lines_in_y; unprocessed_lines -= lines_in_y;
if (unprocessed_lines == 0) { if (unprocessed_lines == 0) {
@@ -7,6 +7,8 @@
#include <algorithm> #include <algorithm>
#include <bitset> #include <bitset>
#include <chrono> #include <chrono>
#include <filesystem>
#include <fstream>
#include <optional> #include <optional>
#include <thread> #include <thread>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
@@ -16,6 +18,8 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/literals.h" #include "common/literals.h"
#include <ranges> #include <ranges>
#include "common/settings.h" #include "common/settings.h"
@@ -393,6 +397,17 @@ std::vector<const char*> ExtensionListForVulkan(
return output; return output;
} }
constexpr std::array<char, 8> STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'};
constexpr u32 STATIC_CACHE_VERSION = 1;
std::filesystem::path StaticPipelineCacheFilename() {
const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
if (!Common::FS::CreateDir(shader_dir)) {
return {};
}
return shader_dir / "vulkan_static_pipelines.bin";
}
} // Anonymous namespace } // Anonymous namespace
void Device::RemoveExtension(bool& extension, const std::string& extension_name) { void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
@@ -780,15 +795,100 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
vk::Check(vmaCreateAllocator(&allocator_info, &allocator)); vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
owns_static_pipeline_cache = surface != VkSurfaceKHR{};
LoadStaticPipelineCache();
// Initialize GPU logging if enabled // Initialize GPU logging if enabled
InitializeGPULogging(); InitializeGPULogging();
} }
Device::~Device() { Device::~Device() {
SaveStaticPipelineCache();
ShutdownGPULogging(); ShutdownGPULogging();
vmaDestroyAllocator(allocator); vmaDestroyAllocator(allocator);
} }
void Device::LoadStaticPipelineCache() {
const auto create = [this](size_t size, const void* data) {
static_pipeline_cache = logical.CreatePipelineCache({
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.initialDataSize = size,
.pInitialData = data,
});
};
if (!owns_static_pipeline_cache) {
create(0, nullptr);
return;
}
const auto filename = StaticPipelineCacheFilename();
if (filename.empty()) {
create(0, nullptr);
return;
}
std::vector<char> data;
try {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
create(0, nullptr);
return;
}
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
const size_t total = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
std::array<char, 8> magic{};
u32 version{};
if (total < magic.size() + sizeof(version)) {
create(0, nullptr);
return;
}
file.read(magic.data(), magic.size())
.read(reinterpret_cast<char*>(&version), sizeof(version));
if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) {
create(0, nullptr);
return;
}
data.resize(total - magic.size() - sizeof(version));
file.read(data.data(), static_cast<std::streamsize>(data.size()));
} catch (const std::ios_base::failure& e) {
create(0, nullptr);
return;
}
create(data.size(), data.empty() ? nullptr : data.data());
}
void Device::SaveStaticPipelineCache() const {
if (!owns_static_pipeline_cache || !static_pipeline_cache) {
return;
}
const auto filename = StaticPipelineCacheFilename();
if (filename.empty()) {
return;
}
size_t size = 0;
std::vector<char> data;
static_pipeline_cache.Read(&size, nullptr);
if (size == 0) {
return;
}
data.resize(size);
static_pipeline_cache.Read(&size, data.data());
try {
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
file.exceptions(std::ofstream::failbit);
if (!file.is_open()) {
return;
}
file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size())
.write(reinterpret_cast<const char*>(&STATIC_CACHE_VERSION),
sizeof(STATIC_CACHE_VERSION))
.write(data.data(), static_cast<std::streamsize>(size));
} catch (const std::ios_base::failure& e) {
Common::FS::RemoveFile(filename);
}
}
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
FormatType format_type) const { FormatType format_type) const {
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
@@ -975,6 +1075,12 @@ bool Device::GetSuitability(bool requires_swapchain) {
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
FOR_EACH_VK_EXTENSION(EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION);
extensions.depth_stencil_resolve =
extensions.depth_stencil_resolve &&
(instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2);
RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve,
VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
@@ -1122,6 +1228,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
SetNext(next, properties.push_descriptor); SetNext(next, properties.push_descriptor);
} }
if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) {
properties.depth_stencil_resolve.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
SetNext(next, properties.depth_stencil_resolve);
}
if (extensions.descriptor_buffer) { if (extensions.descriptor_buffer) {
properties.descriptor_buffer.sType = properties.descriptor_buffer.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
@@ -90,6 +90,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
EXTENSION(EXT, TOOLING_INFO, tooling_info) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
@@ -268,6 +270,10 @@ public:
return physical; return physical;
} }
VkPipelineCache StaticPipelineCache() const noexcept {
return *static_pipeline_cache;
}
/// Returns the main graphics queue. /// Returns the main graphics queue.
vk::Queue GetGraphicsQueue() const { vk::Queue GetGraphicsQueue() const {
return graphics_queue; return graphics_queue;
@@ -607,6 +613,37 @@ FN_MAX_LIMIT_LIST
return extensions.shader_stencil_export; return extensions.shader_stencil_export;
} }
/// Returns true if the device supports VK_KHR_create_renderpass2.
bool IsKhrCreateRenderPass2Supported() const {
return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2;
}
/// Returns true if the device supports VK_KHR_depth_stencil_resolve.
bool IsKhrDepthStencilResolveSupported() const {
return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) &&
IsKhrCreateRenderPass2Supported();
}
/// Returns the supported resolve modes for the depth aspect.
VkResolveModeFlags GetDepthResolveModes() const {
return properties.depth_stencil_resolve.supportedDepthResolveModes;
}
/// Returns the supported resolve modes for the stencil aspect.
VkResolveModeFlags GetStencilResolveModes() const {
return properties.depth_stencil_resolve.supportedStencilResolveModes;
}
/// Returns true if the depth and stencil aspects may resolve with different modes.
bool SupportsIndependentResolve() const {
return properties.depth_stencil_resolve.independentResolve == VK_TRUE;
}
/// Returns true if only one of the depth and stencil aspects may be resolved.
bool SupportsIndependentResolveNone() const {
return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE;
}
/// Returns true if depth/stencil operations can be performed efficiently. /// Returns true if depth/stencil operations can be performed efficiently.
/// Either through shader export or hardware blits. /// Either through shader export or hardware blits.
bool CanPerformDepthStencilOperations() const { bool CanPerformDepthStencilOperations() const {
@@ -957,6 +994,21 @@ FN_MAX_LIMIT_LIST
return features2.features.multiViewport; return features2.features.multiViewport;
} }
VkPipelineStageFlags AttachmentConsumerStages() const {
VkPipelineStageFlags stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (features2.features.geometryShader) {
stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
}
return stages;
}
/// Returns true if the device supports VK_KHR_maintenance1. /// Returns true if the device supports VK_KHR_maintenance1.
bool IsKhrMaintenance1Supported() const { bool IsKhrMaintenance1Supported() const {
return extensions.maintenance1; return extensions.maintenance1;
@@ -1088,6 +1140,9 @@ private:
/// Returns true if the device natively supports blitting depth stencil images. /// Returns true if the device natively supports blitting depth stencil images.
bool TestDepthStencilBlits(VkFormat format) const; bool TestDepthStencilBlits(VkFormat format) const;
void LoadStaticPipelineCache();
void SaveStaticPipelineCache() const;
private: private:
VkInstance instance; ///< Vulkan instance. VkInstance instance; ///< Vulkan instance.
VmaAllocator allocator; ///< VMA allocator. VmaAllocator allocator; ///< VMA allocator.
@@ -1096,6 +1151,8 @@ private:
vk::Device logical; ///< Logical device. vk::Device logical; ///< Logical device.
vk::Queue graphics_queue; ///< Main graphics queue. vk::Queue graphics_queue; ///< Main graphics queue.
vk::Queue present_queue; ///< Main present queue. vk::Queue present_queue; ///< Main present queue.
vk::PipelineCache static_pipeline_cache;
bool owns_static_pipeline_cache{};
u32 instance_version{}; ///< Vulkan instance version. u32 instance_version{}; ///< Vulkan instance version.
u32 graphics_family{}; ///< Main graphics queue family index. u32 graphics_family{}; ///< Main graphics queue family index.
u32 present_family{}; ///< Main present queue family index. u32 present_family{}; ///< Main present queue family index.
@@ -1142,6 +1199,7 @@ private:
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
VkPhysicalDeviceProperties properties{}; VkPhysicalDeviceProperties properties{};
}; };
@@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCreatePipelineLayout); X(vkCreatePipelineLayout);
X(vkCreateQueryPool); X(vkCreateQueryPool);
X(vkCreateRenderPass); X(vkCreateRenderPass);
X(vkCreateRenderPass2);
X(vkCreateSampler); X(vkCreateSampler);
X(vkCreateSemaphore); X(vkCreateSemaphore);
X(vkCreateShaderModule); X(vkCreateShaderModule);
@@ -270,6 +271,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
if (!dld.vkQueueSubmit2) { if (!dld.vkQueueSubmit2) {
Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device); Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device);
} }
// Render pass creation v2 is core in Vulkan 1.2, otherwise requires VK_KHR_create_renderpass2
if (!dld.vkCreateRenderPass2) {
Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device);
}
#undef X #undef X
} }
@@ -725,6 +731,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const {
return RenderPass(object, handle, *dld); return RenderPass(object, handle, *dld);
} }
RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const {
VkRenderPass object;
Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object));
return RenderPass(object, handle, *dld);
}
DescriptorSetLayout Device::CreateDescriptorSetLayout( DescriptorSetLayout Device::CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const { const VkDescriptorSetLayoutCreateInfo& ci) const {
VkDescriptorSetLayout object; VkDescriptorSetLayout object;
@@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateQueryPool vkCreateQueryPool{};
PFN_vkCreateRenderPass vkCreateRenderPass{}; PFN_vkCreateRenderPass vkCreateRenderPass{};
PFN_vkCreateRenderPass2 vkCreateRenderPass2{};
PFN_vkCreateSampler vkCreateSampler{}; PFN_vkCreateSampler vkCreateSampler{};
PFN_vkCreateSemaphore vkCreateSemaphore{}; PFN_vkCreateSemaphore vkCreateSemaphore{};
PFN_vkCreateShaderModule vkCreateShaderModule{}; PFN_vkCreateShaderModule vkCreateShaderModule{};
@@ -1045,6 +1046,8 @@ public:
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
[[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const;
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const; const VkDescriptorSetLayoutCreateInfo& ci) const;
@@ -1526,7 +1529,7 @@ public:
} }
void SetDepthBias(float constant_factor, float clamp, float slope_factor, void SetDepthBias(float constant_factor, float clamp, float slope_factor,
VkDepthBiasRepresentationInfoEXT* extra) const noexcept { const VkDepthBiasRepresentationInfoEXT* extra) const noexcept {
VkDepthBiasInfoEXT info{ VkDepthBiasInfoEXT info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT, .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT,
.pNext = extra, .pNext = extra,
+56 -36
View File
@@ -5,10 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3/SDL_timer.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "common/logging.h" #include "common/logging.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
@@ -30,17 +26,9 @@ EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Co
LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError()); LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError());
exit(1); exit(1);
} }
titlebar_timer = SDL_AddTimer(2000, [](void *userdata, SDL_TimerID, Uint32) -> Uint32 {
auto* this_ = (EmuWindow_SDL3*)userdata;
auto const results = this_->system.GetAndResetPerfStats();
auto const title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, results.emulation_speed * 100.0f);
SDL_SetWindowTitle(this_->render_window, title.c_str());
return 2000;
}, this);
} }
EmuWindow_SDL3::~EmuWindow_SDL3() { EmuWindow_SDL3::~EmuWindow_SDL3() {
SDL_RemoveTimer(titlebar_timer);
system.HIDCore().UnloadInputDevices(); system.HIDCore().UnloadInputDevices();
input_subsystem->Shutdown(); input_subsystem->Shutdown();
SDL_Quit(); SDL_Quit();
@@ -144,7 +132,8 @@ void EmuWindow_SDL3::Fullscreen() {
switch (Settings::values.fullscreen_mode.GetValue()) { switch (Settings::values.fullscreen_mode.GetValue()) {
case Settings::FullscreenMode::Exclusive: case Settings::FullscreenMode::Exclusive:
// Set window size to render size before entering fullscreen in exclusive mode. // Set window size to render size before entering fullscreen in exclusive mode.
if (const SDL_DisplayMode* display_mode_ptr = SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) { if (const SDL_DisplayMode* display_mode_ptr =
SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
display_mode = *display_mode_ptr; display_mode = *display_mode_ptr;
SDL_SetWindowSize(render_window, display_mode.w, display_mode.h); SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
SDL_SetWindowFullscreenMode(render_window, &display_mode); SDL_SetWindowFullscreenMode(render_window, &display_mode);
@@ -176,60 +165,92 @@ void EmuWindow_SDL3::Fullscreen() {
} }
} }
void EmuWindow_SDL3::OnEvent(SDL_Event& event) { void EmuWindow_SDL3::WaitEvent() {
// Notice how we skip the "update title" aspect on most events // Called on main thread
// this is because some WMs do NOT like changing titles while resizing SDL_Event event;
// so let's just... not do that, thanks :)
// Afterall we don't really expect the user to pay attention to the titlebar if (!SDL_WaitEvent(&event)) {
// while they're moving a lot of shit around... const char* error = SDL_GetError();
if (!error || strcmp(error, "") == 0) {
// https://github.com/libsdl-org/SDL/issues/5780
// Sometimes SDL will return without actually having hit an error condition;
// just ignore it in this case.
return;
}
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
exit(1);
}
switch (event.type) { switch (event.type) {
case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
return OnResize(); OnResize();
break;
case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_MINIMIZED:
is_shown = false; is_shown = false;
return OnResize(); OnResize();
break;
case SDL_EVENT_WINDOW_EXPOSED: case SDL_EVENT_WINDOW_EXPOSED:
is_shown = true; is_shown = true;
return OnResize(); OnResize();
break;
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
is_open = false; is_open = false;
return; break;
case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
return OnKeyEvent(int(event.key.scancode), event.key.down ? 1 : 0); OnKeyEvent(static_cast<int>(event.key.scancode), event.key.down ? 1 : 0);
break;
case SDL_EVENT_MOUSE_MOTION: case SDL_EVENT_MOUSE_MOTION:
// ignore if it came from touch // ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseMotion(event.motion.x, event.motion.y); OnMouseMotion(event.motion.x, event.motion.y);
return; break;
case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_EVENT_MOUSE_BUTTON_UP:
// ignore if it came from touch // ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) if (event.button.which != SDL_TOUCH_MOUSEID) {
OnMouseButton(event.button.button, event.button.down ? 1 : 0, s32(event.button.x), s32(event.button.y)); OnMouseButton(event.button.button, event.button.down ? 1 : 0,
return; static_cast<s32>(event.button.x), static_cast<s32>(event.button.y));
}
break;
case SDL_EVENT_FINGER_DOWN: case SDL_EVENT_FINGER_DOWN:
return OnFingerDown(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID)); OnFingerDown(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchID));
break;
case SDL_EVENT_FINGER_MOTION: case SDL_EVENT_FINGER_MOTION:
return OnFingerMotion(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID)); OnFingerMotion(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchID));
break;
case SDL_EVENT_FINGER_UP: case SDL_EVENT_FINGER_UP:
return OnFingerUp(); OnFingerUp();
break;
case SDL_EVENT_QUIT: case SDL_EVENT_QUIT:
is_open = false; is_open = false;
return; break;
default: default:
break; break;
} }
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = system.GetAndResetPerfStats();
const auto title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)",
Common::g_build_fullname,
Common::g_scm_branch,
Common::g_scm_desc,
results.average_game_fps,
results.emulation_speed * 100.0);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
} }
// Credits to Samantas5855 and others for this function. // Credits to Samantas5855 and others for this function.
void EmuWindow_SDL3::SetWindowIcon() { void EmuWindow_SDL3::SetWindowIcon() {
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
// Icons do not work yet
#else
SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size); SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size);
if (yuzu_icon_stream == nullptr) { if (yuzu_icon_stream == nullptr) {
LOG_WARNING(Frontend, "Failed to create Eden icon stream."); LOG_WARNING(Frontend, "Failed to create Eden icon stream.");
@@ -243,7 +264,6 @@ void EmuWindow_SDL3::SetWindowIcon() {
// The icon is attached to the window pointer // The icon is attached to the window pointer
SDL_SetWindowIcon(render_window, window_icon); SDL_SetWindowIcon(render_window, window_icon);
SDL_DestroySurface(window_icon); SDL_DestroySurface(window_icon);
#endif
} }
void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
+6 -7
View File
@@ -9,12 +9,11 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <SDL3/SDL.h>
#include <SDL3/SDL_timer.h>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h" #include "core/frontend/graphics_context.h"
struct SDL_Window;
namespace Core { namespace Core {
class System; class System;
} }
@@ -36,7 +35,7 @@ public:
bool IsShown() const override; bool IsShown() const override;
/// Wait for the next event on the main thread. /// Wait for the next event on the main thread.
void OnEvent(SDL_Event& event); void WaitEvent();
// Sets the window icon from yuzu.bmp // Sets the window icon from yuzu.bmp
void SetWindowIcon(); void SetWindowIcon();
@@ -81,9 +80,6 @@ protected:
/// Called when a configuration change affects the minimal size of the window /// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
/// Periodic changer of titlebar (independent of event loop)
SDL_TimerID titlebar_timer;
/// Is the window still open? /// Is the window still open?
bool is_open = true; bool is_open = true;
@@ -93,6 +89,9 @@ protected:
/// Internal SDL3 render window /// Internal SDL3 render window
SDL_Window* render_window{}; SDL_Window* render_window{};
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
/// Input subsystem to use with this window. /// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem; InputCommon::InputSubsystem* input_subsystem;
+65 -75
View File
@@ -8,12 +8,6 @@
#include <memory> #include <memory>
#include <regex> #include <regex>
#include <string> #include <string>
#include "common/settings_enums.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL_main.h>
#include <fmt/ostream.h> #include <fmt/ostream.h>
@@ -45,7 +39,9 @@
#ifdef _WIN32 #ifdef _WIN32
// windows.h needs to be included before shellapi.h // windows.h needs to be included before shellapi.h
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <shellapi.h>
#include "common/windows/timer_resolution.h" #include "common/windows/timer_resolution.h"
#endif #endif
@@ -178,14 +174,8 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
std::cout << std::endl << "* " << message << std::endl << std::endl; std::cout << std::endl << "* " << message << std::endl << std::endl;
} }
struct SdlState { /// Application entry point
Core::System system{}; int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL3> emu_window;
};
extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
SdlState* state = new SdlState();
#ifdef _WIN32 #ifdef _WIN32
if (AttachConsole(ATTACH_PARENT_PROCESS)) { if (AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CONOUT$", "wb", stdout); freopen("CONOUT$", "wb", stdout);
@@ -203,7 +193,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
if (argv_w == nullptr) { if (argv_w == nullptr) {
LOG_CRITICAL(Frontend, "Failed to get command line arguments"); LOG_CRITICAL(Frontend, "Failed to get command line arguments");
return SDL_APP_FAILURE; return -1;
} }
#endif #endif
std::string filepath; std::string filepath;
@@ -257,7 +247,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
break; break;
case 'h': case 'h':
PrintHelp(argv[0]); PrintHelp(argv[0]);
return SDL_APP_FAILURE; return 0;
case 'g': case 'g':
filepath = std::string(optarg); filepath = std::string(optarg);
break; break;
@@ -274,7 +264,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
if (!std::regex_match(str_arg, re)) { if (!std::regex_match(str_arg, re)) {
std::cout << "Wrong format for option --multiplayer\n"; std::cout << "Wrong format for option --multiplayer\n";
PrintHelp(argv[0]); PrintHelp(argv[0]);
return SDL_APP_FAILURE; return -1;
} }
std::smatch match; std::smatch match;
@@ -289,11 +279,11 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
if (!std::regex_match(nickname, nickname_re)) { if (!std::regex_match(nickname, nickname_re)) {
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters"); LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
return SDL_APP_FAILURE; return -1;
} }
if (address.empty()) { if (address.empty()) {
LOG_ERROR(Frontend, "Address to room must not be empty"); LOG_ERROR(Frontend, "Address to room must not be empty");
return SDL_APP_FAILURE; return -1;
} }
break; break;
} }
@@ -306,7 +296,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
break; break;
case 'v': case 'v':
PrintVersion(); PrintVersion();
return SDL_APP_FAILURE; return -1;
case 'n': case 'n':
force_null_render = true; force_null_render = true;
break; break;
@@ -368,73 +358,79 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
if (filepath.empty()) { if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return SDL_APP_FAILURE; return -1;
} }
state->system.Initialize(); Core::System system{};
system.Initialize();
InputCommon::InputSubsystem input_subsystem{}; InputCommon::InputSubsystem input_subsystem{};
// Apply the command line arguments // Apply the command line arguments
state->system.ApplySettings(); system.ApplySettings();
std::unique_ptr<EmuWindow_SDL3> emu_window;
switch (Settings::values.renderer_backend.GetValue()) { switch (Settings::values.renderer_backend.GetValue()) {
#ifdef HAS_OPENGL #ifdef HAS_OPENGL
case Settings::RendererBackend::OpenGL_GLSL: case Settings::RendererBackend::OpenGL_GLSL:
case Settings::RendererBackend::OpenGL_GLASM: case Settings::RendererBackend::OpenGL_GLASM:
case Settings::RendererBackend::OpenGL_SPIRV: case Settings::RendererBackend::OpenGL_SPIRV:
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, system, fullscreen);
break; break;
#endif #endif
case Settings::RendererBackend::Vulkan: case Settings::RendererBackend::Vulkan:
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, system, fullscreen);
break; break;
case Settings::RendererBackend::Null: case Settings::RendererBackend::Null:
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, system, fullscreen);
break; break;
default: default:
LOG_CRITICAL(Frontend, "Invalid renderer backend"); LOG_CRITICAL(Frontend, "Invalid renderer backend");
return SDL_APP_FAILURE; return -1;
} }
#ifdef _WIN32 #ifdef _WIN32
Common::Windows::SetCurrentTimerResolutionToMaximum(); Common::Windows::SetCurrentTimerResolutionToMaximum();
state->system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution()); system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
#endif #endif
state->system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
state->system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
state->system.GetFileSystemController().CreateFactories(*state->system.GetFilesystem()); system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
state->system.GetUserChannel().clear(); system.GetUserChannel().clear();
Service::AM::FrontendAppletParameters load_parameters{ Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application, .applet_id = Service::AM::AppletId::Application,
}; };
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters); const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};
switch (load_result) { switch (load_result) {
case Core::SystemResultStatus::Success:
break; // Expected case
case Core::SystemResultStatus::ErrorGetLoader: case Core::SystemResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath); LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
return SDL_APP_FAILURE; return -1;
case Core::SystemResultStatus::ErrorLoader: case Core::SystemResultStatus::ErrorLoader:
LOG_CRITICAL(Frontend, "Failed to load ROM!"); LOG_CRITICAL(Frontend, "Failed to load ROM!");
return SDL_APP_FAILURE; return -1;
case Core::SystemResultStatus::ErrorNotInitialized: case Core::SystemResultStatus::ErrorNotInitialized:
LOG_CRITICAL(Frontend, "CPUCore not initialized"); LOG_CRITICAL(Frontend, "CPUCore not initialized");
return SDL_APP_FAILURE; return -1;
case Core::SystemResultStatus::ErrorVideoCore: case Core::SystemResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!"); LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return SDL_APP_FAILURE; return -1;
case Core::SystemResultStatus::Success:
break; // Expected case
default: default:
const u16 loader_id = u16(Core::SystemResultStatus::ErrorLoader); if (static_cast<u32>(load_result) >
const u16 error_id = u16(load_result) - loader_id; static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
LOG_CRITICAL(Frontend, const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
"While attempting to load the ROM requested, an error occurred. Please " const u16 error_id = static_cast<u16>(load_result) - loader_id;
"refer to the Eden wiki for more information or the Eden discord for " LOG_CRITICAL(Frontend,
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", "While attempting to load the ROM requested, an error occurred. Please "
loader_id, error_id, Loader::ResultStatus(error_id)); "refer to the Eden wiki for more information or the Eden discord for "
return SDL_APP_FAILURE; "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
}
break;
} }
if (use_multiplayer) { if (use_multiplayer) {
@@ -443,46 +439,40 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
member->BindOnStatusMessageReceived(OnStatusMessageReceived); member->BindOnStatusMessageReceived(OnStatusMessageReceived);
member->BindOnStateChanged(OnStateChanged); member->BindOnStateChanged(OnStateChanged);
member->BindOnError(OnNetworkError); member->BindOnError(OnNetworkError);
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname); LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
nickname);
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
} else { } else {
LOG_ERROR(Network, "Could not access RoomMember"); LOG_ERROR(Network, "Could not access RoomMember");
return SDL_APP_FAILURE; return 0;
} }
} }
// Core is loaded, start the GPU (makes the GPU contexts current to this thread) // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
state->system.GPU().Start(); system.GPU().Start();
state->system.GetCpuManager().OnGpuReady(); system.GetCpuManager().OnGpuReady();
if (Settings::values.use_disk_shader_cache.GetValue()) { if (Settings::values.use_disk_shader_cache.GetValue()) {
state->system.Renderer().ReadRasterizer()->LoadDiskResources( system.Renderer().ReadRasterizer()->LoadDiskResources(
state->system.GetApplicationProcessProgramID(), std::stop_token{}, system.GetApplicationProcessProgramID(), std::stop_token{},
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
} }
// don't do anything, SDL3 already exists for us :D system.RegisterExitCallback([&] {
state->system.RegisterExitCallback([] {}); // Just exit right away.
void(state->system.Run()); exit(0);
if (state->system.DebuggerEnabled()) });
state->system.InitializeDebugger(); void(system.Run());
return SDL_APP_SUCCESS; if (system.DebuggerEnabled()) {
} system.InitializeDebugger();
extern "C" SDL_AppResult SDL_AppIterate(void *appstate) { }
SdlState *state = (SdlState *)appstate; while (emu_window->IsOpen()) {
return state->emu_window->IsOpen() ? SDL_APP_CONTINUE : SDL_APP_SUCCESS; emu_window->WaitEvent();
} }
extern "C" SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { system.DetachDebugger();
SdlState *state = (SdlState *)appstate; void(system.Pause());
state->emu_window->OnEvent(*event); system.ShutdownMainProcess();
return SDL_APP_SUCCESS; return 0;
}
extern "C" void SDL_AppQuit(void *appstate, SDL_AppResult result) {
SdlState *state = (SdlState *)appstate;
state->system.DetachDebugger();
void(state->system.Pause());
state->system.ShutdownMainProcess();
delete state;
} }
#define VMA_IMPLEMENTATION #define VMA_IMPLEMENTATION