Compare commits

...

128 Commits

Author SHA1 Message Date
CamilleLaVey 14cbcc2438 [vulkan] Removed LineRasterization mode for the removed LineLoop -> LineStrip convertion 2026-03-14 20:23:48 -04:00
CamilleLaVey 0a73fb5229 [vulkan] Changed handling of helper inside MaxwellToVK 2026-03-14 20:11:07 -04:00
CamilleLaVey 254250a83d Fix build 2026-03-14 19:57:14 -04:00
CamilleLaVey 83c727794e [vulkan] Adjust polygon mode handling + topology emulation resolve for line and point with polygon mode 2026-03-14 04:03:30 -04:00
CamilleLaVey a6c610cf26 [vulkan] Changed perspective of use for topologies strip/fan 2026-03-14 01:37:42 -04:00
CamilleLaVey 0dafab4053 [vulkam] Changed function location 2026-03-14 00:34:05 -04:00
CamilleLaVey 40b2fd0e6b [vulkan] Adjusted TFB Topology entry and use 2026-03-14 00:26:55 -04:00
CamilleLaVey 610e9eb516 [vulkan] Adjusted not indexed Quad's handling for QuadStrip + Adjusted Quad's calculations for LUT 2026-03-13 19:33:59 -04:00
CamilleLaVey d835cab32d [vulkan] Adjusting XFB configuration + StreamingByteCount adjustments 2026-03-13 19:17:50 -04:00
CamilleLaVey 46c2a81f4a [vulkan] Removed unneccesary UpdateBuffers from the end session of TFB/ Thanks Pavel 2026-03-13 14:35:26 -04:00
CamilleLaVey 1ebdef1a02 [vulkan] Adjusting how surface clip is working when viewport offset gets a scaling value 2026-03-13 02:54:14 -04:00
CamilleLaVey bd80d603e2 [vulkan] Corrected mishandling of render area + rescaling logic by using wrongly push constants 2026-03-13 01:55:02 -04:00
CamilleLaVey 76bb5fc18b [vulkan] Small patch for tesselation vs an already handling with topology patch list. 2026-03-13 01:17:57 -04:00
CamilleLaVey b1821cb60e [vulkan] Extend ConditionalRendering use vs old hacks for stability in some drivers 2026-03-12 23:30:22 -04:00
CamilleLaVey b1cc417c55 [vulkan] Removed QCOM and Turnip from an older handling in drivers for query pools handled in host-side vs gpu normal path 2026-03-12 21:45:30 -04:00
CamilleLaVey 70620c245c removed unused function 2026-03-12 15:20:43 -04:00
CamilleLaVey 8389a98648 [vulkan] First try to implement a LineLoop to LineStrip resolve via a software emulation for not equivalent lines available in Vulkan 2026-03-12 14:55:52 -04:00
CamilleLaVey 7fe40a37ef [vulkan] Removed bresenham line mode for driver issues + adjusted LineLoop to LineStrip handling 2026-03-12 14:15:07 -04:00
CamilleLaVey e05c74108f fix build 2026-03-12 01:31:30 -04:00
CamilleLaVey cd7414814a [vulkan] Removed handling for PixelFormatInterpretation 2026-03-12 01:13:50 -04:00
CamilleLaVey dfe1f39a4b Revert "[vulkan] Replaced old logic for DescriptorType for a numeric handling per type to avoid mismatches during format binding" 2026-03-12 00:55:49 -04:00
CamilleLaVey 3f1f20185c Revert "[vulkan] simplify numeric type determination" 2026-03-12 00:54:14 -04:00
CamilleLaVey 1845d5b222 Fix build 2026-03-10 05:25:45 -04:00
CamilleLaVey 1240268048 [vulkan] Fix primitive count calculation for Quads and QuadStrip in vk_query_cache 2026-03-10 05:03:45 -04:00
CamilleLaVey a134ad3fbd [vulkan] Added Line loop + topology emulation accuracy increased by changing triangle assumption 2026-03-10 04:58:11 -04:00
CamilleLaVey da30efbc55 [vulkan] Added primitive count calculation based on topology + patch vertices in PrimitivesSucceededStreamer 2026-03-10 04:14:07 -04:00
CamilleLaVey 2dbca791f6 [vulkan] Adjusted synchronization handling in QueryCacheRuntime + ConditionalRendering setting bug with syncing 2026-03-10 03:53:45 -04:00
CamilleLaVey 74248bd35a Removing remanents of NCE changes to previous state 2026-03-10 03:20:05 -04:00
CamilleLaVey e715925d52 Revert "[nce] Added rasterizer caching checks to memory management" 2026-03-10 03:13:21 -04:00
CamilleLaVey 852b8e176f Revert "[nce] Added rasterizer memory handling by nce page faults + intercepted memory access in nce with cached rasterizer data" 2026-03-10 03:12:19 -04:00
CamilleLaVey b2b07abbc8 Revert "[nce] Added "tainted" page fault handling inside dual channel" 2026-03-10 03:10:37 -04:00
CamilleLaVey ce2f2187bd Revert "[nce] Adjusted precise access fault window handling + decay mechanism" 2026-03-10 03:09:30 -04:00
CamilleLaVey 8c077fc4cd Revert "[nce] Added case for access fault handling to manage page edge cases" 2026-03-10 03:01:02 -04:00
CamilleLaVey b88ca5b635 Revert "[nce] Added dual channel handling for guest access faults" 2026-03-10 02:58:57 -04:00
CamilleLaVey 4755ec7a59 [vulkan] simplify numeric type determination 2026-03-10 02:46:35 -04:00
CamilleLaVey e20875d8fc fix build from nce changes 2026-03-09 01:57:29 -04:00
CamilleLaVey 58bff28b04 [vulkan] Reset query pool in Reset() method + removed bank reset 2026-03-09 01:40:26 -04:00
CamilleLaVey 570cd64c03 [nce] Added rasterizer memory handling by nce page faults + intercepted memory access in nce with cached rasterizer data 2026-03-09 01:16:07 -04:00
CamilleLaVey b0a607539a [nce] Added rasterizer caching checks to memory management 2026-03-09 00:34:12 -04:00
CamilleLaVey 64163f2d2a [vulkan] Adjusting few remanants of topologies + review of possible missing dynamic state handling 2026-03-08 22:42:22 -04:00
CamilleLaVey 631ad933b2 fix build 2026-03-08 22:25:56 -04:00
CamilleLaVey 6985aaf614 [vulkan] Adjusted dynamic state handling + update state tracker for extended dynamic states 2026-03-08 22:16:08 -04:00
CamilleLaVey d1466b0e9d Revert "[debug] Instrumentalization for EDS related pipeline worker failure." 2026-03-08 21:13:26 -04:00
CamilleLaVey a02e8a5679 [vulkan] Removed dynamic culling mode/ front face. 2026-03-08 19:52:20 -04:00
CamilleLaVey e98280bee9 [vulkan] Removal of dynamic viewport/scissor 2026-03-08 19:13:09 -04:00
CamilleLaVey 1d0a3e83fa [vulkan] Removed topologies 2026-03-08 17:23:46 -04:00
CamilleLaVey 4343874a69 [vulkan] Removal Primitive Topology 2026-03-08 16:30:03 -04:00
CamilleLaVey 11d5c0f96a [nce] Adjusted precise access fault window handling + decay mechanism 2026-03-08 16:07:05 -04:00
CamilleLaVey ca42578d4e [nce] Added "tainted" page fault handling inside dual channel 2026-03-08 15:55:06 -04:00
CamilleLaVey b7087a16ba [nce] Added dual channel handling for guest access faults 2026-03-08 15:37:29 -04:00
CamilleLaVey 1bdedc17ad [nce] Added case for access fault handling to manage page edge cases 2026-03-08 15:12:47 -04:00
CamilleLaVey 32e63b5fa2 fix build 2026-03-08 12:46:31 -04:00
CamilleLaVey 2e89bb890a [android] Modified compiliance of Android's Surface in older API's 2026-03-08 12:31:49 -04:00
CamilleLaVey 842acf2c86 fix build 2026-03-08 04:46:19 -04:00
CamilleLaVey 2d4c460011 [debug] Instrumentalization for EDS related pipeline worker failure. 2026-03-08 04:38:40 -04:00
CamilleLaVey c42c67ab85 [android] Refined cadence for low frame rate; chrono + duration = clamp/ ms response extended 2026-03-08 04:31:10 -04:00
CamilleLaVey 5bce1993e1 [android] Linked Frame Time Cadence to Quantized Frames 2026-03-08 04:17:58 -04:00
CamilleLaVey 8c79a3a8de [android] Adjusted heuristics hints for SpeedLimit x ScreenRefresh + Quantized FrameRate calculation 2026-03-08 04:05:42 -04:00
CamilleLaVey ebb3cda782 [android] Quantization for frames + hints Surface 2026-03-08 03:56:58 -04:00
CamilleLaVey daf972b517 [android] Replaced FramePacingMode path + Surface/EmuWindow added hints for FrameRate 2026-03-08 03:41:42 -04:00
CamilleLaVey cd25e61afa Fix license headers 2026-03-08 01:58:38 -04:00
CamilleLaVey 4a7aa1618a [vulkan] Add InvalidateState for old topology draw 2026-03-08 01:48:23 -04:00
CamilleLaVey 1aae75512b Revert "[vulkan] Added conservative path for RoundingModeRTZ + instrumentalization for shaders use" 2026-03-08 01:21:36 -04:00
CamilleLaVey 825b899bf8 Revert "fix build" 2026-03-08 01:18:06 -04:00
CamilleLaVey 73c16c3d45 Revert "[debug] Added extra logging/ address for shader info -> FP32Mul Optimize Path" 2026-03-08 01:17:13 -04:00
CamilleLaVey 124c97a88a Revert "[debug] fix logging entries for histogram" 2026-03-08 01:10:06 -04:00
CamilleLaVey 02f9c5ffd2 Revert "[test] Histogram debug - shader float control -> initial target: Adreno" 2026-03-08 01:07:59 -04:00
CamilleLaVey 042a10cf71 Revert "[test] shader float control returned to Adreno" 2026-03-08 01:04:14 -04:00
CamilleLaVey d6a889828f [vulkan] Invalidate scheduler state after rendering in multiple Vulkan draw functions 2026-03-08 01:02:37 -04:00
CamilleLaVey d06eb3f52f [vulkan] Changed info.color_output_type gate 2026-03-07 06:46:15 +00:00
CamilleLaVey 21c77d5dce [vulkan] Added conservative path for RoundingModeRTZ + instrumentalization for shaders use 2026-03-07 06:46:15 +00:00
CamilleLaVey a80e0f10ba Just meow 2026-03-07 06:46:15 +00:00
CamilleLaVey ccb518dc05 Changed logging context 2026-03-07 06:46:15 +00:00
CamilleLaVey bb768ad570 fix building 2 2026-03-07 06:46:15 +00:00
CamilleLaVey c028d925bc fix build 2026-03-07 06:46:15 +00:00
CamilleLaVey 3557ff28b7 [vulkan] Added no depth vs depth compare support 2026-03-07 06:46:15 +00:00
CamilleLaVey 108bb3d28b [debug] Added extra logging/ address for shader info -> FP32Mul Optimize Path 2026-03-07 06:46:15 +00:00
CamilleLaVey 5b12a7725d fix build 2026-03-07 06:46:15 +00:00
CamilleLaVey 15d575aa31 [test] Change forcerd order for CompareMask + forced refresh/ emit 2026-03-07 06:46:15 +00:00
CamilleLaVey 47fe86be7b [vulkan] Extended 3D image handling for subresource range calculations 2026-03-07 06:46:14 +00:00
CamilleLaVey 3db45f3c46 [vulkan] Implemented active color output tracking in runtime info and update fragment color handling 2026-03-07 06:46:14 +00:00
CamilleLaVey 4481391474 [vulkan] Maintenance9 removal 2026-03-07 06:46:14 +00:00
CamilleLaVey dee102cf92 [debug] Added extra parametters for histogram track info -> shader info for RZ 2026-03-07 06:46:14 +00:00
CamilleLaVey ad2f40b0e7 [debug] fix logging entries for histogram 2026-03-07 06:46:14 +00:00
CamilleLaVey d10080b757 [test] Histogram debug - shader float control -> initial target: Adreno 2026-03-07 06:46:14 +00:00
CamilleLaVey ce15cf7cd3 [vulkan] Adjusted image view usage flags to ensure compatibility with image format in TextureCache 2026-03-07 06:46:14 +00:00
CamilleLaVey 6b87b0052a [test] shader float control returned to Adreno 2026-03-07 06:46:14 +00:00
CamilleLaVey 9630da580d [vulkan] Removed counter enable for ZPassPixelCount64 in Clear method and added initial layout transition for images in RefreshContents 2026-03-07 06:46:14 +00:00
CamilleLaVey 12fdd88a58 smol fix for query enable 2026-03-07 06:46:14 +00:00
CamilleLaVey 646aea7fbf [vulkan] Removed unused helper in texture pass 2026-03-07 06:46:14 +00:00
CamilleLaVey 186c0b0cc7 [vulkan] Replaced old logic for DescriptorType for a numeric handling per type to avoid mismatches during format binding 2026-03-07 06:46:14 +00:00
CamilleLaVey ee1ffbaf2e [vulkan] Adjustments to wrong access of image-memory barrier on depth fragments + blending extended enabling method 2026-03-07 06:46:14 +00:00
CamilleLaVey c931de0570 [vulkan] Adjusted QueryReset's 2026-03-07 06:46:14 +00:00
CamilleLaVey 33f1fb1cf4 fix build 2026-03-07 06:46:14 +00:00
CamilleLaVey aabc470314 First meow in honor of meowly 2026-03-07 06:46:14 +00:00
CamilleLaVey d82a6a273d fix build 2026-03-07 06:46:14 +00:00
lizzie 4bb853d52a Merge fix 2026-03-07 06:46:14 +00:00
CamilleLaVey 43ebdb1ffc [vulkan] Adjusted DYNAMIC_STATES setting 2026-03-07 06:46:14 +00:00
CamilleLaVey e310f0b151 [vulkan] Indirect draw for dstStageMask 2026-03-07 06:46:14 +00:00
CamilleLaVey 9b915c8659 [vulkan] Adjusting re-cast for EDS support when bind happens 2026-03-07 06:46:14 +00:00
CamilleLaVey 35ab33de6a Fix build 2026-03-07 06:46:14 +00:00
CamilleLaVey 5615ea9ced [vulkan] re-cast vkCmdSet for dynamic states during binding 2026-03-07 06:46:14 +00:00
CamilleLaVey d273fc4ad6 [vulkan] Changed UpdateDynamicState order 2026-03-07 06:46:14 +00:00
CamilleLaVey e3e880e879 [vulkan] Added flag to detect last mode from provokingVertex 2026-03-07 06:46:14 +00:00
CamilleLaVey 6b8115f27a fix meow 2026-03-07 06:46:14 +00:00
CamilleLaVey a24e7e8143 [maxwell] Adding storage flags to some surface format 2026-03-07 06:46:14 +00:00
CamilleLaVey 88b9393b44 [vulkan] Adding guards per dynamic states setters 2026-03-07 06:46:14 +00:00
CamilleLaVey 2081d659d2 [vulkan] Dead code removal from VertexInputDynamicState 2026-03-07 06:46:14 +00:00
CamilleLaVey 9eacaf2444 [vulkan] Adjustment for Viewport and Scissor counts within EDS 2026-03-07 06:46:01 +00:00
CamilleLaVey c9eb764d2a [vulkan] Fixing some incongruences with pipeline keys and dynamic state flags 2026-03-07 06:46:01 +00:00
CamilleLaVey 72ced6b947 [vulkan] Set always vertex strides 2026-03-07 06:46:01 +00:00
CamilleLaVey ffae2350ca [vulkan] fix custom border color query struct 2026-03-07 06:46:01 +00:00
CamilleLaVey 646542a397 [vulkan] Query custom border color properties based on device report 2026-03-07 06:46:01 +00:00
CamilleLaVey 1ae76d44c1 [vulkan, rasterizer] Filling missing byte count handling when TFB is not available 2026-03-07 06:46:01 +00:00
CamilleLaVey 24d07ab28c fix build 2026-03-07 06:46:01 +00:00
CamilleLaVey 651a999017 [vulkan] Unique representation logic fix. 2026-03-07 06:46:01 +00:00
CamilleLaVey 5c0e12fb0e [vulkan] Rework line rasterization handle 2026-03-07 06:46:01 +00:00
CamilleLaVey 712c505cd1 [vulkan] Extending conversative rasterization detection and handling 2026-03-07 06:46:01 +00:00
CamilleLaVey 051522b54e [vulkan] Fix conditional rendering enable 2026-03-07 06:46:01 +00:00
CamilleLaVey a1fdbef129 [vulkan] removing dead code for driverID detection under EDS handling/ban 2026-03-07 06:46:01 +00:00
CamilleLaVey ecd5c751f8 fix license headers 2026-03-07 06:46:01 +00:00
CamilleLaVey 69678d02b6 [android] Removing unneeded setting 2026-03-07 06:46:01 +00:00
CamilleLaVey 8d031532d8 [vulkan] Dead code removal 2026-03-07 06:46:01 +00:00
CamilleLaVey c8e4818b01 [vulkan] Fixing inconsistences within VK_EXT_extended_dynamic_state1 handling 2026-03-07 06:46:01 +00:00
CamilleLaVey 2bac9cec32 [vulkan] Implenting layouts use for indexing descriptors 2026-03-07 06:46:01 +00:00
CamilleLaVey 6190fcaaef [vulkan] Changing ProvokingVertex enabling nature 2026-03-07 06:46:01 +00:00
CamilleLaVey 3272e1fcb5 [vulkan] adjusting BindVertexBuffer2EXT wrong calling in pipeline 2026-03-07 06:46:00 +00:00
CamilleLaVey 8faeffdc7e [vulkan] removal of EDS3 and VIDS 2026-03-07 06:44:48 +00:00
74 changed files with 2265 additions and 1385 deletions
@@ -30,8 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"),
RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"),
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
RENDERER_SAMPLE_SHADING("sample_shading"),
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
@@ -141,20 +141,6 @@ abstract class SettingsItem(
valuesId = R.array.dynaStateValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_PROVOKING_VERTEX,
titleId = R.string.provoking_vertex,
descriptionId = R.string.provoking_vertex_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
titleId = R.string.vertex_input_dynamic_state,
descriptionId = R.string.vertex_input_dynamic_state_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DESCRIPTOR_INDEXING,
@@ -349,15 +335,6 @@ abstract class SettingsItem(
valuesId = R.array.astcDecodingMethodValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_ASTC_RECOMPRESSION,
titleId = R.string.astc_recompression,
descriptionId = R.string.astc_recompression_description,
choicesId = R.array.astcRecompressionMethodNames,
valuesId = R.array.astcRecompressionMethodValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_VRAM_USAGE_MODE,
@@ -670,15 +647,6 @@ abstract class SettingsItem(
valuesId = R.array.dmaAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.FRAME_PACING_MODE,
titleId = R.string.frame_pacing_mode,
descriptionId = R.string.frame_pacing_mode_description,
choicesId = R.array.framePacingModeNames,
valuesId = R.array.framePacingModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
@@ -267,11 +267,9 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.DMA_ACCURACY.key)
add(IntSetting.FRAME_PACING_MODE.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
add(IntSetting.RENDERER_ASTC_RECOMPRESSION.key)
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
@@ -291,8 +289,6 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.extensions))
add(IntSetting.RENDERER_DYNA_STATE.key)
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key)
add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key)
add(IntSetting.RENDERER_SAMPLE_SHADING.key)
@@ -94,8 +94,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
// Since Android 15, google automatically forces "games" to be 60 hrz
// This ensures the display's max refresh rate is actually used
display?.let {
val supportedModes = it.supportedModes
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }
@@ -6,8 +6,15 @@
#include <android/native_window_jni.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <dlfcn.h>
#include "common/android/id_cache.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "input_common/drivers/android.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
@@ -22,6 +29,12 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = 0;
m_window_height = 0;
window_info.render_surface = nullptr;
m_last_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint_votes = 0;
m_smoothed_present_rate = 0.0f;
m_last_frame_display_time = {};
m_pending_frame_rate_since = {};
return;
}
@@ -32,6 +45,7 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
window_info.render_surface = reinterpret_cast<void*>(surface);
UpdateFrameRateHint();
}
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
@@ -51,6 +65,9 @@ void EmuWindow_Android::OnTouchReleased(int id) {
}
void EmuWindow_Android::OnFrameDisplayed() {
UpdateObservedFrameRate();
UpdateFrameRateHint();
if (!m_first_frame) {
Common::Android::RunJNIOnFiber<void>(
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
@@ -58,6 +75,175 @@ void EmuWindow_Android::OnFrameDisplayed() {
}
}
void EmuWindow_Android::UpdateObservedFrameRate() {
const auto now = Clock::now();
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
const float seconds = frame_time.count();
if (seconds > 0.0f) {
const float instantaneous_rate = 1.0f / seconds;
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
instantaneous_rate <= 240.0f) {
constexpr float SmoothingFactor = 0.15f;
if (m_smoothed_present_rate <= 0.0f) {
m_smoothed_present_rate = instantaneous_rate;
} else {
m_smoothed_present_rate +=
(instantaneous_rate - m_smoothed_present_rate) * SmoothingFactor;
}
}
}
}
m_last_frame_display_time = now;
}
float EmuWindow_Android::QuantizeFrameRateHint(float frame_rate) {
if (!std::isfinite(frame_rate) || frame_rate <= 0.0f) {
return 0.0f;
}
frame_rate = std::clamp(frame_rate, 1.0f, 240.0f);
constexpr float Step = 0.5f;
return std::round(frame_rate / Step) * Step;
}
float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
if (!EmulationSession::GetInstance().IsRunning()) {
return 0.0f;
}
const double frame_time_scale =
EmulationSession::GetInstance().System().GetPerfStats().GetLastFrameTimeScale();
if (!std::isfinite(frame_time_scale) || frame_time_scale <= 0.0) {
return 0.0f;
}
const float verified_rate =
std::clamp(60.0f / static_cast<float>(frame_time_scale), 0.0f, 240.0f);
return QuantizeFrameRateHint(verified_rate);
}
float EmuWindow_Android::GetFrameRateHint() const {
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
const float frame_time_verified_hint = GetFrameTimeVerifiedHint();
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
if (std::fabs(observed_rate - m_last_frame_rate_hint) <= tolerance) {
return m_last_frame_rate_hint;
}
}
const float observed_hint = QuantizeFrameRateHint(observed_rate);
if (observed_hint > 0.0f) {
if (frame_time_verified_hint > 0.0f) {
const float tolerance = std::max(observed_hint * 0.20f, 3.0f);
if (std::fabs(observed_hint - frame_time_verified_hint) <= tolerance) {
return QuantizeFrameRateHint((observed_hint + frame_time_verified_hint) * 0.5f);
}
}
return observed_hint;
}
if (frame_time_verified_hint > 0.0f) {
return frame_time_verified_hint;
}
constexpr float NominalFrameRate = 60.0f;
if (!Settings::values.use_speed_limit.GetValue()) {
return NominalFrameRate;
}
const u16 speed_limit = Settings::SpeedLimit();
if (speed_limit == 0) {
return 0.0f;
}
const float speed_limited_rate =
NominalFrameRate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
return QuantizeFrameRateHint(speed_limited_rate);
}
void EmuWindow_Android::UpdateFrameRateHint() {
auto* const surface = reinterpret_cast<ANativeWindow*>(window_info.render_surface);
if (!surface) {
return;
}
const auto now = Clock::now();
const float frame_rate_hint = GetFrameRateHint();
if (std::fabs(frame_rate_hint - m_last_frame_rate_hint) < 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
return;
}
if (frame_rate_hint == 0.0f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = now;
} else if (m_last_frame_rate_hint >= 0.0f) {
if (std::fabs(frame_rate_hint - m_pending_frame_rate_hint) >= 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 1;
m_pending_frame_rate_since = now;
return;
}
++m_pending_frame_rate_hint_votes;
if (m_pending_frame_rate_since.time_since_epoch().count() == 0) {
m_pending_frame_rate_since = now;
}
const auto stable_for = now - m_pending_frame_rate_since;
const float reference_rate = std::max(frame_rate_hint, 1.0f);
const auto stable_duration = std::chrono::duration_cast<Clock::duration>(
std::chrono::duration<float>(std::clamp(3.0f / reference_rate, 0.15f, 0.40f)));
constexpr std::uint32_t MinStableVotes = 3;
if (m_pending_frame_rate_hint_votes < MinStableVotes || stable_for < stable_duration) {
return;
}
} else {
m_pending_frame_rate_since = now;
}
using SetFrameRateWithChangeStrategyFn =
int32_t (*)(ANativeWindow*, float, int8_t, int8_t);
using SetFrameRateFn = int32_t (*)(ANativeWindow*, float, int8_t);
static const auto set_frame_rate_with_change_strategy =
reinterpret_cast<SetFrameRateWithChangeStrategyFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRateWithChangeStrategy"));
static const auto set_frame_rate = reinterpret_cast<SetFrameRateFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRate"));
constexpr int8_t FrameRateCompatibilityDefault = 0;
constexpr int8_t ChangeFrameRateOnlyIfSeamless = 0;
int32_t result = -1;
if (set_frame_rate_with_change_strategy) {
result = set_frame_rate_with_change_strategy(surface, frame_rate_hint,
FrameRateCompatibilityDefault,
ChangeFrameRateOnlyIfSeamless);
} else if (set_frame_rate) {
result = set_frame_rate(surface, frame_rate_hint, FrameRateCompatibilityDefault);
} else {
return;
}
if (result != 0) {
LOG_DEBUG(Frontend, "Failed to update Android surface frame rate hint: {}", result);
return;
}
m_last_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
}
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library)
: m_driver_library{driver_library} {
@@ -1,8 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
@@ -50,10 +55,24 @@ public:
};
private:
using Clock = std::chrono::steady_clock;
void UpdateFrameRateHint();
void UpdateObservedFrameRate();
[[nodiscard]] float GetFrameRateHint() const;
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
float m_window_width{};
float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
float m_last_frame_rate_hint = -1.0f;
float m_pending_frame_rate_hint = -1.0f;
float m_smoothed_present_rate = 0.0f;
Clock::time_point m_last_frame_display_time{};
Clock::time_point m_pending_frame_rate_since{};
std::uint32_t m_pending_frame_rate_hint_votes = 0;
};
@@ -506,8 +506,6 @@
<string name="dyna_state">الحالة الديناميكية الموسعة</string>
<string name="dyna_state_description">يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في حالة الديناميكية الموسعة. تسمح الأرقام الأعلى بمزيد من الميزات ويمكن أن تزيد من الأداء، ولكنها قد تسبب مشاكل مع بعض برامج التشغيل والأجهزة.</string>
<string name="disabled">معطل</string>
<string name="vertex_input_dynamic_state">حالة ديناميكية لإدخال الرأس</string>
<string name="vertex_input_dynamic_state_description">يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء.</string>
<string name="provoking_vertex">الرأس المثير</string>
<string name="provoking_vertex_description">يحسن الإضاءة ومعالجة الرؤوس في بعض الألعاب. مدعوم فقط على وحدات معالجة الرسومات Vulkan 1.0+.</string>
<string name="descriptor_indexing">فهرسة الوصف</string>
@@ -488,8 +488,6 @@
<string name="dyna_state">Úroveň EDS</string>
<string name="dyna_state_description">Určuje počet funkcí využívaných v rámci rozšířeného dynamického stavu API Vulkan (Extended Dynamic State). Vyšší hodnoty umožňují využít více funkcí a mohou zvýšit výkon, ale u některých ovladačů a výrobců grafických karet mohou způsobovat problémy s kompatibilitou.</string>
<string name="disabled">Vypnuto</string>
<string name="vertex_input_dynamic_state">Dynamický stav vstupu vrcholů (Vertex Input)</string>
<string name="vertex_input_dynamic_state_description">Aktivuje funkci dynamického stavu vstupu vrcholů (Vertex Input Dynamic State) pro lepší kvalitu a výkon.</string>
<string name="provoking_vertex">Určující vrchol</string>
<string name="provoking_vertex_description">Zlepšuje osvětlení a zpracování vrcholů v některých hrách. Podporováno pouze na GPU s API Vulkan 1.0+.</string>
<string name="descriptor_indexing">Indexování deskriptorů</string>
@@ -486,8 +486,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
<string name="dyna_state">Erweiterter dynamischer Status</string>
<string name="dyna_state_description">Steuert die Anzahl der Funktionen, die im \"Vertex Input Dynamic State\" werden können. Höhere Werte ermöglichen mehr Funktionen und können die Leistung steigern, können aber bei einigen Treibern und Anbietern zu Problemen führen.</string>
<string name="disabled">Deaktiviert</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Aktiviert die Funktion \"Vertex Input Dynamic State\" für bessere Qualität und Leistung.</string>
<string name="provoking_vertex">Provokanter Vertex</string>
<string name="provoking_vertex_description">Verbessert die Beleuchtung und die Vertex-Verarbeitung in einigen Spielen. Wird nur von GPUs mit Vulkan 1.0+ unterstützt.</string>
<string name="descriptor_indexing">Deskriptor-Indizierung</string>
@@ -436,8 +436,6 @@
<string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques.</string>
<string name="dyna_state">État dynamique étendu</string>
<string name="disabled">Désactivé</string>
<string name="vertex_input_dynamic_state">État dynamique d\'entrée de sommet</string>
<string name="vertex_input_dynamic_state_description">Active la fonctionnalité d\'état dynamique des entrées de sommets pour une meilleure qualité et de meilleures performances.</string>
<string name="provoking_vertex">Provoque des Vertex</string>
<string name="provoking_vertex_description">Améliore l`éclairage et la gestion des vertex dans certains jeux. Pris en charge uniquement par les GPU Vulkan 1.0+.</string>
<string name="descriptor_indexing">Indexation des descripteurs</string>
@@ -488,8 +488,6 @@
<string name="dyna_state">Rozszerzony stan dynamiczny</string>
<string name="dyna_state_description">Kontroluje liczbę funkcji, które mogą być używane w Extended Dynamic State. Wyższe wartości pozwalają na użycie większej liczby funkcji i mogą zwiększyć wydajność, ale mogą powodować problemy z niektórymi sterownikami i u niektórych producentów.</string>
<string name="disabled">Wyłączone</string>
<string name="vertex_input_dynamic_state">Dynamiczny stan wejścia wierzchołków</string>
<string name="vertex_input_dynamic_state_description">Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność.</string>
<string name="provoking_vertex">Wierzchołek prowokujący</string>
<string name="provoking_vertex_description">Poprawia oświetlenie i obsługę wierzchołków w niektórych grach. Obsługiwane tylko przez GPU Vulkan 1.0+.</string>
<string name="descriptor_indexing">Indeksowanie deskryptorów</string>
@@ -471,8 +471,6 @@
<string name="renderer_asynchronous_shaders_description">Compila shaders de forma assíncrona. Isso pode reduzir engasgos, mas também pode introduzir falhas gráficas.</string>
<string name="dyna_state">Extended Dynamic State</string>
<string name="disabled">Desativado</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Ativa o recurso de vertex input dynamic state para melhor qualidade e desempenho.</string>
<string name="provoking_vertex">Provoking Vertex</string>
<string name="provoking_vertex_description">Vértice Provocante: Melhora a iluminação e o processamento de vértices em certos jogos. Suportado apenas em GPUs com Vulkan 1.0 ou superior.</string>
<string name="descriptor_indexing">Descriptor Indexing</string>
@@ -498,8 +498,6 @@
<string name="dyna_state">Расширенное динамическое состояние</string>
<string name="dyna_state_description">Управляет количеством функций, доступных в режиме «Расширенное динамическое состояние». Большее число позволяет задействовать больше функций и может повысить производительность, но способно вызывать проблемы с некоторыми драйверами и графикой.</string>
<string name="disabled">Отключено</string>
<string name="vertex_input_dynamic_state">Динамическое состояние ввода вершин</string>
<string name="vertex_input_dynamic_state_description">Включает функцию динамического состояния ввода вершин для повышения качества и производительности</string>
<string name="provoking_vertex">Определяющая вершина</string>
<string name="provoking_vertex_description">Улучшает освещение и обработку вершин в некоторых играх. Поддерживается только ГПУ с Vulkan 1.0+.</string>
<string name="descriptor_indexing">Индексирование дескрипторов</string>
@@ -502,8 +502,6 @@
<string name="dyna_state">Розширений динамічний стан</string>
<string name="dyna_state_description">Керує кількістю функцій, які можна використовувати в «Розширеному динамічному стані». Вище число дозволяє більше функцій і може покращити продуктивність, але може спричинити проблеми з деякими драйверами й виробниками.</string>
<string name="disabled">Вимкнено</string>
<string name="vertex_input_dynamic_state">Динамічний стан введення вершин</string>
<string name="vertex_input_dynamic_state_description">Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності.</string>
<string name="provoking_vertex">Провокативна вершина</string>
<string name="provoking_vertex_description">Покращує освітлення та взаємодію з вершинами у деяких іграх. Лише для ГП з підтримкою Vulkan 1.0+.</string>
<string name="descriptor_indexing">Індексація дескрипторів</string>
@@ -496,8 +496,6 @@
<string name="dyna_state">扩展动态状态</string>
<string name="dyna_state_description">控制在扩展动态状态中可使用的函数数量。更高的数值允许启用更多功能,并可能提升性能,但同时也可能导致额外的图形问题。</string>
<string name="disabled">已禁用</string>
<string name="vertex_input_dynamic_state">顶点输入动态状态</string>
<string name="vertex_input_dynamic_state_description">开启顶点输入动态状态功能来获得更好的质量和性能。</string>
<string name="provoking_vertex">引发顶点</string>
<string name="provoking_vertex_description">改善某些游戏中的光照和顶点处理。仅支持Vulkan 1.0+ GPU。</string>
<string name="descriptor_indexing">描述符索引</string>
@@ -467,8 +467,6 @@
<string name="renderer_asynchronous_shaders_description">非同步編譯著色器。這可能會減少卡頓,但也可能導致圖形錯誤。</string>
<string name="dyna_state">擴展動態狀態</string>
<string name="disabled">已停用</string>
<string name="vertex_input_dynamic_state">頂點輸入動態狀態</string>
<string name="vertex_input_dynamic_state_description">啟用頂點輸入動態狀態以取得更佳的品質及性能</string>
<string name="provoking_vertex">引發頂點</string>
<string name="provoking_vertex_description">改善某些遊戲中的光照和頂點處理。僅支援Vulkan 1.0+ GPU。</string>
<string name="descriptor_indexing">描述符索引</string>
@@ -632,14 +632,12 @@
<item>@string/disabled</item>
<item>ExtendedDynamicState 1</item>
<item>ExtendedDynamicState 2</item>
<item>ExtendedDynamicState 3</item>
</string-array>
<integer-array name="dynaStateValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
<string-array name="installKeysResults">
@@ -531,8 +531,6 @@
<string name="dyna_state">Extended Dynamic State</string>
<string name="dyna_state_description">Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors.</string>
<string name="disabled">Disabled</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Enables vertex input dynamic state feature for better quality and performance.</string>
<string name="provoking_vertex">Provoking Vertex</string>
<string name="provoking_vertex_description">Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.</string>
<string name="descriptor_indexing">Descriptor Indexing</string>
+2 -10
View File
@@ -453,7 +453,7 @@ struct Values {
Category::RendererAdvanced};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,
AstcDecodeMode::Gpu,
#else
AstcDecodeMode::Gpu,
#endif
@@ -586,7 +586,7 @@ struct Values {
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
#if defined (ANDROID) || defined (__APPLE__)
ExtendedDynamicState::Disabled,
ExtendedDynamicState::EDS1,
#else
ExtendedDynamicState::EDS2,
#endif
@@ -601,14 +601,6 @@ struct Values {
Category::RendererExtensions,
Specialization::Scalar};
SwitchableSetting<bool> vertex_input_dynamic_state{linkage,
#if defined (ANDROID)
false,
#else
true,
#endif
"vertex_input_dynamic_state", Category::RendererExtensions};
SwitchableSetting<bool> provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions};
SwitchableSetting<bool> descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions};
Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
+1 -1
View File
@@ -154,7 +154,7 @@ ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge)
ENUM(GpuUnswizzle, VeryLow, Low, Normal, Medium, High)
ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2);
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
ENUM(GameListMode, TreeView, GridView);
ENUM(SpeedMode, Standard, Turbo, Slow);
@@ -368,17 +368,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
"Higher states allow for more features and can increase performance, but may cause "
"additional graphical issues."));
INSERT(Settings,
vertex_input_dynamic_state,
tr("Vertex Input Dynamic State"),
tr("Enables vertex input dynamic state feature for better quality and performance."));
INSERT(Settings,
provoking_vertex,
tr("Provoking Vertex"),
tr("Improves lighting and vertex handling in some games.\n"
"Only Vulkan 1.0+ devices support this extension."));
INSERT(Settings,
descriptor_indexing,
tr("Descriptor Indexing"),
@@ -799,7 +788,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
PAIR(ExtendedDynamicState, Disabled, tr("Disabled")),
PAIR(ExtendedDynamicState, EDS1, tr("ExtendedDynamicState 1")),
PAIR(ExtendedDynamicState, EDS2, tr("ExtendedDynamicState 2")),
PAIR(ExtendedDynamicState, EDS3, tr("ExtendedDynamicState 3")),
}});
translations->insert({Settings::EnumMetadata<Settings::GameListMode>::Index(),
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -31,7 +31,7 @@ struct RenderAreaLayout {
};
constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area);
constexpr u32 RENDERAREA_LAYOUT_OFFSET = sizeof(RescalingLayout);
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
IR::Program& program, Bindings& bindings, bool optimize);
@@ -491,6 +491,9 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
}
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
if (!ctx.runtime_info.active_color_outputs[index]) {
return;
}
const Id component_id{ctx.Const(component)};
const AttributeType type{ctx.runtime_info.color_output_types[index]};
if (type == AttributeType::Float) {
@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -17,6 +17,7 @@
#include "common/div_ceil.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
namespace {
@@ -1095,7 +1096,8 @@ void EmitContext::DefineRenderArea(const Info& info) {
Decorate(push_constant_struct, spv::Decoration::Block);
Name(push_constant_struct, "RenderAreaInfo");
MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0);
MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset,
RENDERAREA_LAYOUT_OFFSET);
MemberName(push_constant_struct, render_are_member_index, "render_area");
const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
@@ -1671,8 +1673,10 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
case Stage::Fragment:
for (u32 index = 0; index < 8; ++index) {
const bool need_dual_source = runtime_info.dual_source_blend && index <= 1;
if (!need_dual_source && !info.stores_frag_color[index] &&
!profile.need_declared_frag_colors) {
const bool should_declare = runtime_info.active_color_outputs[index] &&
(info.stores_frag_color[index] ||
profile.need_declared_frag_colors);
if (!need_dual_source && !should_declare) {
continue;
}
const Id type{GetAttributeType(*this, runtime_info.color_output_types[index])};
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -200,11 +200,10 @@ static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
}
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
const ConstBufferAddr& cbuf) {
const ConstBufferAddr& cbuf) {
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
}
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
if (const IR::Inst* key = v.InstRecursive()) {
+3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+3
View File
@@ -111,6 +111,9 @@ struct RuntimeInfo {
/// Output types for each color attachment
std::array<AttributeType, 8> color_output_types{};
/// Fragment color outputs that are active for the current pipeline.
std::array<bool, 8> active_color_outputs{true, true, true, true, true, true, true, true};
/// Dual source blending
bool dual_source_blend{};
};
+57 -14
View File
@@ -356,11 +356,22 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
if (is_indexed) {
BindHostIndexBuffer();
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const auto& regs = maxwell3d->regs;
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const bool allow_polygon_mode_emulation =
regs.transform_feedback_enabled == 0 &&
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) &&
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation);
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
draw_state.vertex_buffer.count);
const auto polygon_mode = allow_polygon_mode_emulation
? regs.polygon_mode_front
: Maxwell::PolygonMode::Fill;
if (polygon_mode != Maxwell::PolygonMode::Point) {
runtime.BindQuadIndexBuffer(draw_state.topology, polygon_mode,
draw_state.vertex_buffer.first,
draw_state.vertex_buffer.count);
}
}
}
BindHostVertexBuffers();
@@ -760,8 +771,35 @@ void BufferCache<P>::BindHostIndexBuffer() {
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
runtime.BindIndexBuffer(buffer, new_offset, size);
} else {
const auto& regs = maxwell3d->regs;
const bool allow_polygon_mode_emulation =
regs.transform_feedback_enabled == 0 &&
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) &&
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation);
auto effective_topology = draw_state.topology;
auto polygon_mode = allow_polygon_mode_emulation ? regs.polygon_mode_front
: Maxwell::PolygonMode::Fill;
if (allow_polygon_mode_emulation) {
switch (draw_state.topology) {
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
if (polygon_mode == Maxwell::PolygonMode::Point) {
effective_topology = Maxwell::PrimitiveTopology::Points;
} else if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
polygon_mode == Maxwell::PolygonMode::Line) {
effective_topology = Maxwell::PrimitiveTopology::LineStrip;
}
break;
default:
polygon_mode = Maxwell::PolygonMode::Fill;
break;
}
} else {
polygon_mode = Maxwell::PolygonMode::Fill;
}
buffer.MarkUsage(offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
runtime.BindIndexBuffer(effective_topology, polygon_mode, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count,
buffer, offset, size);
}
@@ -1064,13 +1102,23 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
if (maxwell3d->regs.transform_feedback_enabled == 0) {
return;
}
HostBindings<typename P::Buffer> host_bindings;
Buffer& null_buffer = slot_buffers[NULL_BUFFER_ID];
bool reached_end = false;
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = channel_state->transform_feedback_buffers[index];
if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
maxwell3d->regs.transform_feedback.controls[index].stride == 0) {
break;
const auto& control = maxwell3d->regs.transform_feedback.controls[index];
if (reached_end ||
(control.varying_count == 0 && control.stride == 0)) {
reached_end = true;
runtime.BindTransformFeedbackBuffer(index, null_buffer, 0, 0);
continue;
}
const Binding& binding = channel_state->transform_feedback_buffers[index];
if (binding.size == 0 || binding.buffer_id == NULL_BUFFER_ID) {
runtime.BindTransformFeedbackBuffer(index, null_buffer, 0, 0);
continue;
}
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
@@ -1080,12 +1128,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(size);
}
if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings);
runtime.BindTransformFeedbackBuffer(index, buffer, offset, size);
}
}
+50
View File
@@ -12,6 +12,7 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "shader_recompiler/program_header.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
@@ -22,6 +23,37 @@
namespace Tegra::Engines {
namespace {
[[nodiscard]] Maxwell3D::Regs::PrimitiveTopology PrimitiveTopologyFromGeometryOutput(
Shader::OutputTopology topology) {
switch (topology) {
case Shader::OutputTopology::PointList:
return Maxwell3D::Regs::PrimitiveTopology::Points;
case Shader::OutputTopology::LineStrip:
return Maxwell3D::Regs::PrimitiveTopology::LineStrip;
case Shader::OutputTopology::TriangleStrip:
return Maxwell3D::Regs::PrimitiveTopology::TriangleStrip;
}
return Maxwell3D::Regs::PrimitiveTopology::Triangles;
}
[[nodiscard]] Maxwell3D::Regs::PrimitiveTopology PrimitiveTopologyFromTessellationOutput(
Maxwell3D::Regs::Tessellation::OutputPrimitives topology) {
switch (topology) {
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points:
return Maxwell3D::Regs::PrimitiveTopology::Points;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines:
return Maxwell3D::Regs::PrimitiveTopology::Lines;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW:
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW:
return Maxwell3D::Regs::PrimitiveTopology::Triangles;
}
return Maxwell3D::Regs::PrimitiveTopology::Triangles;
}
} // namespace
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
@@ -582,6 +614,8 @@ void Maxwell3D::ProcessQueryCondition() {
break;
}
}
rasterizer->SetConditionalRenderingPredicate(execute_on);
}
void Maxwell3D::ProcessCounterReset() {
@@ -664,6 +698,22 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
return tsc_entry;
}
Maxwell3D::Regs::PrimitiveTopology Maxwell3D::GetTransformFeedbackOutputTopology() const {
if (regs.IsShaderConfigEnabled(Regs::ShaderType::Geometry)) {
const GPUVAddr shader_addr = regs.program_region.Address() +
regs.pipelines[static_cast<size_t>(Regs::ShaderType::Geometry)]
.offset;
Shader::ProgramHeader sph{};
memory_manager.ReadBlockUnsafe(shader_addr, &sph, sizeof(sph));
return PrimitiveTopologyFromGeometryOutput(sph.common3.output_topology.Value());
}
if (regs.IsShaderConfigEnabled(Regs::ShaderType::Tessellation)) {
return PrimitiveTopologyFromTessellationOutput(
regs.tessellation.params.output_primitives.Value());
}
return draw_manager->GetDrawState().topology;
}
u32 Maxwell3D::GetRegisterValue(u32 method) const {
ASSERT(method < Regs::NUM_REGS && "Invalid Maxwell3D register");
return regs.reg_array[method];
+2
View File
@@ -3093,6 +3093,8 @@ public:
return *rasterizer;
}
[[nodiscard]] Regs::PrimitiveTopology GetTransformFeedbackOutputTopology() const;
struct DirtyState {
using Flags = std::bitset<(std::numeric_limits<u8>::max)()>;
using Table = std::array<u8, Regs::NUM_REGS>;
+5
View File
@@ -381,6 +381,11 @@ void QueryCacheBase<Traits>::NotifySegment(bool resume) {
}
}
template <typename Traits>
void QueryCacheBase<Traits>::SetHostConditionalRenderingResult(bool condition_passed) {
impl->runtime.SetHostConditionalRenderingResult(condition_passed);
}
template <typename Traits>
bool QueryCacheBase<Traits>::AccelerateHostConditionalRendering() {
bool qc_dirty = false;
@@ -115,6 +115,8 @@ public:
void NotifySegment(bool resume);
void SetHostConditionalRenderingResult(bool condition_passed);
void BindToChannel(s32 id) override;
protected:
+5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -146,6 +149,8 @@ public:
return false;
}
virtual void SetConditionalRenderingPredicate(bool enabled) {}
/// Attempt to use a faster method to perform a surface copy
[[nodiscard]] virtual bool AccelerateSurfaceCopy(
const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst,
+17 -3
View File
@@ -56,16 +56,23 @@ namespace {
[[nodiscard]] VkImageSubresourceRange SubresourceRangeFromView(const ImageView& image_view) {
auto range = image_view.range;
const bool is_3d_image = image_view.type == VideoCommon::ImageViewType::e3D ||
(image_view.flags & VideoCommon::ImageViewFlagBits::Slice) !=
VideoCommon::ImageViewFlagBits{};
if ((image_view.flags & VideoCommon::ImageViewFlagBits::Slice) != VideoCommon::ImageViewFlagBits{}) {
range.base.layer = 0;
range.extent.layers = 1;
}
u32 layer_count = static_cast<u32>(range.extent.layers);
if (is_3d_image && layer_count == 1) {
layer_count = VK_REMAINING_ARRAY_LAYERS;
}
return VkImageSubresourceRange{
.aspectMask = AspectMaskFromFormat(image_view.format),
.baseMipLevel = static_cast<u32>(range.base.level),
.levelCount = static_cast<u32>(range.extent.levels),
.baseArrayLayer = static_cast<u32>(range.base.layer),
.layerCount = static_cast<u32>(range.extent.layers),
.layerCount = layer_count,
};
}
@@ -170,8 +177,14 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_BLEND_CONSTANTS};
constexpr std::array DYNAMIC_STATES{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
};
constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -589,6 +602,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
});
scheduler.InvalidateState();
}
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -50,6 +50,7 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
});
state.varyings = regs.stream_out_layout;
}
} // Anonymous namespace
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
@@ -60,9 +61,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
reserved_dynamic_state_3_blend.Assign(0);
reserved_dynamic_state_3_enables.Assign(0);
reserved_bit_5.Assign(0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
@@ -103,43 +104,22 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
point_size = std::bit_cast<u32>(regs.point_size);
if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
if (features.has_dynamic_vertex_input) {
// Dirty flag will be reset by the command buffer update
static constexpr std::array LUT{
0u, // Invalid
1u, // SignedNorm
1u, // UnsignedNorm
2u, // SignedInt
3u, // UnsignedInt
1u, // UnsignedScaled
1u, // SignedScaled
1u, // Float
};
const auto& attrs = regs.vertex_attrib_format;
attribute_types = 0;
for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
const u32 mask = attrs[i].constant != 0 ? 0 : 3;
const u32 type = LUT[static_cast<size_t>(attrs[i].type.Value())];
attribute_types |= static_cast<u64>(type & mask) << (i * 2);
}
} else {
maxwell3d.dirty.flags[Dirty::VertexInput] = false;
enabled_divisors = 0;
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index);
binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0;
enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
const auto& input = regs.vertex_attrib_format[index];
auto& attribute = attributes[index];
attribute.raw = 0;
attribute.enabled.Assign(input.constant ? 0 : 1);
attribute.buffer.Assign(input.buffer);
attribute.offset.Assign(input.offset);
attribute.type.Assign(static_cast<u32>(input.type.Value()));
attribute.size.Assign(static_cast<u32>(input.size.Value()));
}
maxwell3d.dirty.flags[Dirty::VertexInput] = false;
enabled_divisors = 0;
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index);
binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0;
enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
const auto& input = regs.vertex_attrib_format[index];
auto& attribute = attributes[index];
attribute.raw = 0;
attribute.enabled.Assign(input.constant ? 0 : 1);
attribute.buffer.Assign(input.buffer);
attribute.offset.Assign(input.offset);
attribute.type.Assign(static_cast<u32>(input.type.Value()));
attribute.size.Assign(static_cast<u32>(input.size.Value()));
}
}
if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) {
@@ -151,26 +131,18 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
}
dynamic_state.raw1 = 0;
dynamic_state.raw2 = 0;
if (!extended_dynamic_state) {
dynamic_state.Refresh(regs);
std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
return static_cast<u16>(array.stride.Value());
});
}
if (!extended_dynamic_state_2_logic_op) {
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {
if (maxwell3d.dirty.flags[Dirty::Blending]) {
maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index);
}
dynamic_state.Refresh(regs);
std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
return static_cast<u16>(array.stride.Value());
});
dynamic_state.Refresh2(regs, topology_, false);
if (maxwell3d.dirty.flags[Dirty::Blending]) {
maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index);
}
}
if (!extended_dynamic_state_3_enables) {
dynamic_state.Refresh3(regs);
}
dynamic_state.Refresh3(regs);
if (xfb_enabled) {
RefreshXfbState(xfb_state, regs);
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -25,9 +25,6 @@ struct DynamicFeatures {
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_logic_op;
bool has_extended_dynamic_state_2_patch_control_points;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_vertex_input;
};
struct FixedPipelineState {
@@ -191,9 +188,9 @@ struct FixedPipelineState {
BitField<0, 1, u32> extended_dynamic_state;
BitField<1, 1, u32> extended_dynamic_state_2;
BitField<2, 1, u32> extended_dynamic_state_2_logic_op;
BitField<3, 1, u32> extended_dynamic_state_3_blend;
BitField<4, 1, u32> extended_dynamic_state_3_enables;
BitField<5, 1, u32> dynamic_vertex_input;
BitField<3, 1, u32> reserved_dynamic_state_3_blend;
BitField<4, 1, u32> reserved_dynamic_state_3_enables;
BitField<5, 1, u32> reserved_bit_5;
BitField<6, 1, u32> xfb_enabled;
BitField<7, 1, u32> ndc_minus_one_to_one;
BitField<8, 2, u32> polygon_mode;
@@ -225,10 +222,7 @@ struct FixedPipelineState {
u32 point_size;
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
union {
u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
u64 enabled_divisors;
};
u64 enabled_divisors;
DynamicState dynamic_state;
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
@@ -260,25 +254,11 @@ struct FixedPipelineState {
// When transform feedback is enabled, use the whole struct
return sizeof(*this);
}
if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
// Exclude dynamic state and attributes
return offsetof(FixedPipelineState, dynamic_state);
}
if (dynamic_vertex_input) {
// Exclude dynamic state
return offsetof(FixedPipelineState, attributes);
}
if (extended_dynamic_state) {
// Exclude dynamic state
return offsetof(FixedPipelineState, vertex_strides);
}
// Default
// Always include the cached dynamic-state payload in the key. Some members of
// `dynamic_state` still feed static pipeline state even when EDS is enabled,
// and excluding the whole block causes incorrect pipeline reuse.
return offsetof(FixedPipelineState, xfb_state);
}
u32 DynamicAttributeType(size_t index) const noexcept {
return (attribute_types >> (index * 2)) & 0b11;
}
};
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -315,15 +315,40 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) {
return {};
}
[[nodiscard]] static bool IsUnsupportedAreaTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
return true;
default:
return false;
}
}
VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
Maxwell::PrimitiveTopology topology) {
Maxwell::PrimitiveTopology topology,
Maxwell::PolygonMode polygon_mode,
bool allow_polygon_mode_emulation) {
if (allow_polygon_mode_emulation && IsUnsupportedAreaTopology(topology)) {
switch (polygon_mode) {
case Maxwell::PolygonMode::Point:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Maxwell::PolygonMode::Line:
return topology == Maxwell::PrimitiveTopology::Polygon
? VK_PRIMITIVE_TOPOLOGY_LINE_STRIP
: VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Maxwell::PolygonMode::Fill:
break;
}
}
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Maxwell::PrimitiveTopology::Lines:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Maxwell::PrimitiveTopology::LineLoop:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PrimitiveTopology::LineStrip:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PrimitiveTopology::Triangles:
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -49,7 +49,10 @@ struct FormatInfo {
VkShaderStageFlagBits ShaderStage(Shader::Stage stage);
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology,
Maxwell::PolygonMode polygon_mode =
Maxwell::PolygonMode::Fill,
bool allow_polygon_mode_emulation = false);
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
Maxwell::VertexAttribute::Size size);
@@ -15,6 +15,7 @@
#include "shader_recompiler/shader_info.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"
#include "video_core/vulkan_common/vulkan_device.h"
@@ -30,23 +31,58 @@ public:
return device->IsKhrPushDescriptorSupported() &&
num_descriptors <= device->MaxPushDescriptors();
}
// TODO(crueter): utilize layout binding flags
vk::DescriptorSetLayout CreateDescriptorSetLayout(bool use_push_descriptor) const {
if (bindings.empty()) {
return nullptr;
}
variable_descriptor_count = 0;
binding_flags.clear();
VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.pNext = nullptr,
.bindingCount = 0,
.pBindingFlags = nullptr,
};
const bool use_descriptor_indexing =
!use_push_descriptor && device->isExtDescriptorIndexingSupported();
const void* layout_next = nullptr;
if (use_descriptor_indexing) {
binding_flags.assign(bindings.size(), 0);
for (size_t i = 0; i < bindings.size(); ++i) {
if (bindings[i].descriptorCount > 1) {
binding_flags[i] |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;
}
}
if (bindings.back().descriptorCount > 1) {
binding_flags.back() |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT;
variable_descriptor_count = bindings.back().descriptorCount;
}
binding_flags_ci.bindingCount = static_cast<u32>(binding_flags.size());
binding_flags_ci.pBindingFlags = binding_flags.data();
layout_next = &binding_flags_ci;
}
const VkDescriptorSetLayoutCreateFlags flags =
use_push_descriptor ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR : 0;
return device->GetLogical().CreateDescriptorSetLayout({
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.pNext = layout_next,
.flags = flags,
.bindingCount = static_cast<u32>(bindings.size()),
.pBindings = bindings.data(),
});
}
u32 VariableDescriptorCount() const noexcept {
return variable_descriptor_count;
}
vk::DescriptorUpdateTemplate CreateTemplate(VkDescriptorSetLayout descriptor_set_layout,
VkPipelineLayout pipeline_layout,
bool use_push_descriptor) const {
@@ -134,8 +170,10 @@ private:
bool is_compute{};
boost::container::small_vector<VkDescriptorSetLayoutBinding, 32> bindings;
boost::container::small_vector<VkDescriptorUpdateTemplateEntry, 32> entries;
mutable boost::container::small_vector<VkDescriptorBindingFlags, 32> binding_flags;
u32 binding{};
u32 num_descriptors{};
mutable u32 variable_descriptor_count{};
size_t offset{};
};
@@ -195,10 +233,16 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
const VkImageView vk_image_view{image_view.Handle(desc.type)};
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
const auto surface_type{VideoCore::Surface::GetFormatType(image_view.format)};
const bool allow_depth_compare =
desc.is_depth && (surface_type == VideoCore::Surface::SurfaceType::Depth ||
surface_type == VideoCore::Surface::SurfaceType::DepthStencil);
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()};
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
: sampler.Handle()};
const VkSampler vk_sampler{use_fallback_sampler
? sampler.HandleWithDefaultAnisotropy(
allow_depth_compare)
: sampler.Handle(allow_depth_compare)};
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
}
@@ -206,6 +206,7 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_i
TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
});
scheduler.InvalidateState();
return *images.image_views[Rcas];
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -140,6 +143,7 @@ void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
cmdbuf.EndRenderPass();
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
});
scheduler.InvalidateState();
*inout_image = *image.image;
*inout_image_view = *image.image_view;
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -272,6 +272,7 @@ void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
cmdbuf.EndRenderPass();
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
});
scheduler.InvalidateState();
*inout_image = *images.images[Output];
*inout_image_view = *images.image_views[Output];
@@ -461,6 +461,9 @@ static vk::Pipeline CreateWrappedPipelineImpl(
constexpr std::array dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
};
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -101,6 +104,7 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
cmdbuf.EndRenderPass();
});
scheduler.InvalidateState();
}
VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() {
@@ -161,10 +161,11 @@ public:
index_type = IndexTypeFromNumElements(device, num_indices);
const u32 num_quads = GetQuadsNum(num_indices);
const u32 num_triangle_indices = num_quads * 6;
const u32 num_generated_indices = num_quads * GetIndicesPerPrimitive();
const u32 num_first_offset_copies = 4;
const size_t bytes_per_index = BytesPerIndex(index_type);
const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
const size_t size_bytes =
num_generated_indices * bytes_per_index * num_first_offset_copies;
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -189,12 +190,12 @@ public:
}();
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
const size_t quad_size = bytes_per_index * 6;
const size_t primitive_size = bytes_per_index * GetIndicesPerPrimitive();
for (u32 first = 0; first < num_first_offset_copies; ++first) {
for (u32 quad = 0; quad < num_quads; ++quad) {
MakeAndUpdateIndices(staging_data, quad_size, quad, first);
staging_data += quad_size;
MakeAndUpdateIndices(staging_data, primitive_size, quad, first);
staging_data += primitive_size;
}
}
@@ -231,15 +232,20 @@ public:
const VkIndexType index_type_ = index_type;
const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices);
const size_t offset =
(sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type);
(sub_first_offset + GetFirstOffsetQuads(first)) *
static_cast<size_t>(GetIndicesPerPrimitive()) * BytesPerIndex(index_type);
scheduler.Record([buffer_ = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) {
cmdbuf.BindIndexBuffer(buffer_, offset, index_type_);
});
}
protected:
virtual u32 GetIndicesPerPrimitive() const = 0;
virtual u32 GetQuadsNum(u32 num_indices) const = 0;
virtual u32 GetFirstOffsetQuads(u32 first) const = 0;
virtual void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) = 0;
const Device& device;
@@ -262,10 +268,18 @@ public:
~QuadArrayIndexBuffer() = default;
private:
u32 GetIndicesPerPrimitive() const override {
return 6;
}
u32 GetQuadsNum(u32 num_indices_) const override {
return num_indices_ / 4;
}
u32 GetFirstOffsetQuads(u32 first) const override {
return first / 4;
}
template <typename T>
static std::array<T, 6> MakeIndices(u32 quad, u32 first) {
std::array<T, 6> indices{0, 1, 2, 0, 2, 3};
@@ -302,10 +316,18 @@ public:
~QuadStripIndexBuffer() = default;
private:
u32 GetIndicesPerPrimitive() const override {
return 6;
}
u32 GetQuadsNum(u32 num_indices_) const override {
return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0;
}
u32 GetFirstOffsetQuads(u32 first) const override {
return (first / 4) * 2;
}
template <typename T>
static std::array<T, 6> MakeIndices(u32 quad, u32 first) {
std::array<T, 6> indices{0, 3, 1, 0, 2, 3};
@@ -333,6 +355,104 @@ private:
}
};
class QuadArrayLineIndexBuffer : public QuadIndexBuffer {
public:
QuadArrayLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
Scheduler& scheduler_, StagingBufferPool& staging_pool_)
: QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
~QuadArrayLineIndexBuffer() = default;
private:
u32 GetIndicesPerPrimitive() const override {
return 8;
}
u32 GetQuadsNum(u32 num_indices_) const override {
return num_indices_ / 4;
}
u32 GetFirstOffsetQuads(u32 first) const override {
return first / 4;
}
template <typename T>
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
std::array<T, 8> indices{0, 1, 1, 2, 2, 3, 3, 0};
for (T& index : indices) {
index = static_cast<T>(first + index + quad * 4);
}
return indices;
}
void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad,
u32 first) override {
switch (index_type) {
case VK_INDEX_TYPE_UINT8_EXT:
std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT16:
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT32:
std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), primitive_size);
break;
default:
ASSERT(false);
break;
}
}
};
class QuadStripLineIndexBuffer : public QuadIndexBuffer {
public:
QuadStripLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
Scheduler& scheduler_, StagingBufferPool& staging_pool_)
: QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
~QuadStripLineIndexBuffer() = default;
private:
u32 GetIndicesPerPrimitive() const override {
return 8;
}
u32 GetQuadsNum(u32 num_indices_) const override {
return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0;
}
u32 GetFirstOffsetQuads(u32 first) const override {
return (first / 4) * 2;
}
template <typename T>
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
std::array<T, 8> indices{0, 1, 1, 3, 3, 2, 2, 0};
for (T& index : indices) {
index = static_cast<T>(first + index + quad * 2);
}
return indices;
}
void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad,
u32 first) override {
switch (index_type) {
case VK_INDEX_TYPE_UINT8_EXT:
std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT16:
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT32:
std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), primitive_size);
break;
default:
ASSERT(false);
break;
}
}
};
BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_,
Scheduler& scheduler_, StagingBufferPool& staging_pool_,
GuestDescriptorQueue& guest_descriptor_queue_,
@@ -347,7 +467,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if (limit_dynamic_storage_buffers) {
max_dynamic_storage_buffers = device.GetMaxDescriptorSetStorageBuffersDynamic();
}
}
if (device.SupportsUint8Indices()) {
uint8_pass = std::make_unique<Uint8Pass>(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue);
@@ -356,6 +476,10 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
scheduler_, staging_pool_);
quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
scheduler_, staging_pool_);
quad_array_line_index_buffer = std::make_shared<QuadArrayLineIndexBuffer>(
device_, memory_allocator_, scheduler_, staging_pool_);
quad_strip_line_index_buffer = std::make_shared<QuadStripLineIndexBuffer>(
device_, memory_allocator_, scheduler_, staging_pool_);
}
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
@@ -450,8 +574,16 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
}
cmdbuf.CopyBuffer(src_buffer, dst_buffer, VideoCommon::FixSmallVectorADL(vk_copies));
if (barrier) {
// Buffer reads can go to vertex input, shaders, or compute
const VkPipelineStageFlags dst_stages =
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
dst_stages, 0, WRITE_BARRIER);
}
});
}
@@ -479,7 +611,14 @@ void BufferCacheRuntime::PostCopyBarrier() {
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
const VkPipelineStageFlags dst_stages =
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stages,
0, WRITE_BARRIER);
});
}
@@ -506,17 +645,37 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, READ_BARRIER);
cmdbuf.FillBuffer(dest_buffer, offset, size, value);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
// Buffer reads can go to vertex input, shaders, or compute
const VkPipelineStageFlags dst_stages_clear =
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stages_clear,
0, WRITE_BARRIER);
});
}
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
u32 base_vertex, u32 num_indices, VkBuffer buffer,
u32 offset, [[maybe_unused]] u32 size) {
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
IndexFormat index_format, u32 base_vertex,
u32 num_indices, VkBuffer buffer, u32 offset,
[[maybe_unused]] u32 size) {
VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format);
VkDeviceSize vk_offset = offset;
VkBuffer vk_buffer = buffer;
if (polygon_mode == PolygonMode::Line &&
(topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip)) {
if (topology == PrimitiveTopology::Quads) {
quad_array_line_index_buffer->UpdateBuffer(num_indices);
quad_array_line_index_buffer->BindBuffer(base_vertex);
} else {
quad_strip_line_index_buffer->UpdateBuffer(num_indices);
quad_strip_line_index_buffer->BindBuffer(base_vertex);
}
return;
}
if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) {
vk_index_type = VK_INDEX_TYPE_UINT32;
std::tie(vk_buffer, vk_offset) =
@@ -542,7 +701,8 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
});
}
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) {
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
u32 first, u32 count) {
if (count == 0) {
ReserveNullBuffer();
scheduler.Record([this](vk::CommandBuffer cmdbuf) {
@@ -551,6 +711,17 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
return;
}
if (polygon_mode == PolygonMode::Line) {
if (topology == PrimitiveTopology::Quads) {
quad_array_line_index_buffer->UpdateBuffer(first + count);
quad_array_line_index_buffer->BindBuffer(first);
} else if (topology == PrimitiveTopology::QuadStrip) {
quad_strip_line_index_buffer->UpdateBuffer(first + count);
quad_strip_line_index_buffer->BindBuffer(first);
}
return;
}
if (topology == PrimitiveTopology::Quads) {
quad_array_index_buffer->UpdateBuffer(first + count);
quad_array_index_buffer->BindBuffer(first);
@@ -564,23 +735,14 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
if (index >= device.GetMaxVertexInputBindings()) {
return;
}
if (device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
const VkDeviceSize vk_stride = stride;
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
});
} else {
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
ReserveNullBuffer();
buffer = *null_buffer;
offset = 0;
}
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffer(index, buffer, offset);
});
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
ReserveNullBuffer();
buffer = *null_buffer;
offset = 0;
}
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffer(index, buffer, offset);
});
}
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
@@ -604,15 +766,12 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
if (binding_count == 0) {
return;
}
if (device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data());
});
} else {
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data());
});
}
scheduler.Record([bindings_ = std::move(bindings),
buffer_handles_ = std::move(buffer_handles),
binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
bindings_.offsets.data());
});
}
void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -72,12 +72,15 @@ private:
class QuadArrayIndexBuffer;
class QuadStripIndexBuffer;
class QuadArrayLineIndexBuffer;
class QuadStripLineIndexBuffer;
class BufferCacheRuntime {
friend Buffer;
using PrimitiveTopology = Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology;
using IndexFormat = Tegra::Engines::Maxwell3D::Regs::IndexFormat;
using PolygonMode = Tegra::Engines::Maxwell3D::Regs::PolygonMode;
public:
explicit BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_manager_,
@@ -118,15 +121,21 @@ public:
void ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value);
void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices,
u32 base_vertex, VkBuffer buffer, u32 offset, u32 size);
void BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
IndexFormat index_format, u32 base_vertex, u32 num_indices,
VkBuffer buffer, u32 offset, u32 size);
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
void BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, u32 first,
u32 count);
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size) {
BindTransformFeedbackBuffer(index, buffer.Handle(), offset, size);
}
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
@@ -177,11 +186,13 @@ private:
std::shared_ptr<QuadArrayIndexBuffer> quad_array_index_buffer;
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
std::shared_ptr<QuadArrayLineIndexBuffer> quad_array_line_index_buffer;
std::shared_ptr<QuadStripLineIndexBuffer> quad_strip_line_index_buffer;
vk::Buffer null_buffer;
std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass;
QuadIndexedPass quad_index_pass;
bool limit_dynamic_storage_buffers = false;
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
@@ -9,6 +9,7 @@
#include <numeric>
#include <optional>
#include <utility>
#include <vector>
#include "video_core/renderer_vulkan/vk_texture_cache.h"
@@ -237,9 +238,37 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code,
std::optional<u32> optional_subgroup_size)
: device{device_} {
u32 variable_descriptor_count{};
std::vector<VkDescriptorBindingFlags> binding_flags;
VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.pNext = nullptr,
.bindingCount = 0,
.pBindingFlags = nullptr,
};
const bool use_descriptor_indexing = device.isExtDescriptorIndexingSupported();
const void* layout_next = nullptr;
if (use_descriptor_indexing && !bindings.empty()) {
binding_flags.assign(bindings.size(), 0);
for (size_t i = 0; i < bindings.size(); ++i) {
if (bindings[i].descriptorCount > 1) {
binding_flags[i] |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;
}
}
const size_t last_binding = bindings.size() - 1;
if (bindings[last_binding].descriptorCount > 1) {
binding_flags[last_binding] |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT;
variable_descriptor_count = bindings[last_binding].descriptorCount;
}
binding_flags_ci.bindingCount = static_cast<u32>(binding_flags.size());
binding_flags_ci.pBindingFlags = binding_flags.data();
layout_next = &binding_flags_ci;
}
descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout({
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.pNext = layout_next,
.flags = 0,
.bindingCount = bindings.size(),
.pBindings = bindings.data(),
@@ -266,7 +295,8 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
.pipelineLayout = *layout,
.set = 0,
});
descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, bank_info);
descriptor_allocator =
descriptor_pool.Allocator(*descriptor_set_layout, bank_info, variable_descriptor_count);
}
if (code.empty()) {
return;
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -88,9 +88,10 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) {
}
DescriptorAllocator::DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_,
DescriptorBank& bank_, VkDescriptorSetLayout layout_)
DescriptorBank& bank_, VkDescriptorSetLayout layout_,
u32 variable_descriptor_count_)
: ResourcePool(master_semaphore_, SETS_GROW_RATE), device{&device_}, bank{&bank_},
layout{layout_} {}
layout{layout_}, variable_descriptor_count{variable_descriptor_count_} {}
VkDescriptorSet DescriptorAllocator::Commit() {
const size_t index = CommitResource();
@@ -103,9 +104,25 @@ void DescriptorAllocator::Allocate(size_t begin, size_t end) {
vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) {
const std::vector<VkDescriptorSetLayout> layouts(count, layout);
std::vector<u32> variable_descriptor_counts;
VkDescriptorSetVariableDescriptorCountAllocateInfo variable_descriptor_count_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
.pNext = nullptr,
.descriptorSetCount = 0,
.pDescriptorCounts = nullptr,
};
const void* allocate_next = nullptr;
if (variable_descriptor_count != 0) {
variable_descriptor_counts.assign(count, variable_descriptor_count);
variable_descriptor_count_info.descriptorSetCount = static_cast<u32>(count);
variable_descriptor_count_info.pDescriptorCounts = variable_descriptor_counts.data();
allocate_next = &variable_descriptor_count_info;
}
VkDescriptorSetAllocateInfo allocate_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = nullptr,
.pNext = allocate_next,
.descriptorPool = *bank->pools.back(),
.descriptorSetCount = static_cast<u32>(count),
.pSetLayouts = layouts.data(),
@@ -131,18 +148,22 @@ DescriptorPool::DescriptorPool(const Device& device_, Scheduler& scheduler)
DescriptorPool::~DescriptorPool() = default;
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
std::span<const Shader::Info> infos) {
return Allocator(layout, MakeBankInfo(infos));
std::span<const Shader::Info> infos,
u32 variable_descriptor_count) {
return Allocator(layout, MakeBankInfo(infos), variable_descriptor_count);
}
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
const Shader::Info& info) {
return Allocator(layout, MakeBankInfo(std::array{info}));
const Shader::Info& info,
u32 variable_descriptor_count) {
return Allocator(layout, MakeBankInfo(std::array{info}), variable_descriptor_count);
}
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
const DescriptorBankInfo& info) {
return DescriptorAllocator(device, master_semaphore, Bank(info), layout);
const DescriptorBankInfo& info,
u32 variable_descriptor_count) {
return DescriptorAllocator(device, master_semaphore, Bank(info), layout,
variable_descriptor_count);
}
DescriptorBank& DescriptorPool::Bank(const DescriptorBankInfo& reqs) {
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -47,7 +50,8 @@ public:
private:
explicit DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_,
DescriptorBank& bank_, VkDescriptorSetLayout layout_);
DescriptorBank& bank_, VkDescriptorSetLayout layout_,
u32 variable_descriptor_count_);
void Allocate(size_t begin, size_t end) override;
@@ -56,6 +60,7 @@ private:
const Device* device{};
DescriptorBank* bank{};
VkDescriptorSetLayout layout{};
u32 variable_descriptor_count{};
std::vector<vk::DescriptorSets> sets;
};
@@ -69,9 +74,12 @@ public:
DescriptorPool(const DescriptorPool&) = delete;
DescriptorAllocator Allocator(VkDescriptorSetLayout layout,
std::span<const Shader::Info> infos);
DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const Shader::Info& info);
DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const DescriptorBankInfo& info);
std::span<const Shader::Info> infos,
u32 variable_descriptor_count = 0);
DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const Shader::Info& info,
u32 variable_descriptor_count = 0);
DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const DescriptorBankInfo& info,
u32 variable_descriptor_count = 0);
private:
DescriptorBank& Bank(const DescriptorBankInfo& reqs);
@@ -23,6 +23,7 @@
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/surface.h"
#include "video_core/shader_notify.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/vulkan_common/vulkan_device.h"
@@ -100,6 +101,37 @@ bool IsLine(VkPrimitiveTopology topology) {
return std::ranges::find(line_topologies, topology) != line_topologies.end();
}
VkPrimitiveTopology DynamicTopologyClassRepresentative(VkPrimitiveTopology topology) {
switch (topology) {
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
return topology;
}
}
bool SupportsStaticPrimitiveRestart(const Device& device, VkPrimitiveTopology topology) {
if (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
return device.IsPatchListPrimitiveRestartSupported();
}
return SupportsPrimitiveRestart(topology) || device.IsTopologyListPrimitiveRestartSupported();
}
VkViewportSwizzleNV UnpackViewportSwizzle(u16 swizzle) {
union Swizzle {
u32 raw;
@@ -273,7 +305,8 @@ GraphicsPipeline::GraphicsPipeline(
descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
if (!uses_push_descriptor) {
descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, stage_infos);
descriptor_allocator = descriptor_pool.Allocator(
*descriptor_set_layout, stage_infos, builder.VariableDescriptorCount());
}
const VkDescriptorSetLayout set_layout{*descriptor_set_layout};
@@ -477,8 +510,15 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto& info{stage_infos[stage]};
if (info.uses_render_area) {
render_area.uses_render_area = true;
render_area.words = {static_cast<float>(regs.surface_clip.width),
static_cast<float>(regs.surface_clip.height)};
const bool is_rescaling{texture_cache.IsRescaling()};
const auto& resolution = Settings::values.resolution_info;
const float render_area_width = static_cast<float>(
is_rescaling ? resolution.ScaleUp(static_cast<u32>(regs.surface_clip.width))
: static_cast<u32>(regs.surface_clip.width));
const float render_area_height = static_cast<float>(
is_rescaling ? resolution.ScaleUp(static_cast<u32>(regs.surface_clip.height))
: static_cast<u32>(regs.surface_clip.height));
render_area.words = {render_area_width, render_area_height};
}
}};
if constexpr (Spec::enabled_stages[0]) {
@@ -568,47 +608,39 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
}
void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
FixedPipelineState::DynamicState dynamic{};
if (!key.state.extended_dynamic_state) {
dynamic = key.state.dynamic_state;
} else {
dynamic.raw1 = key.state.dynamic_state.raw1;
}
const FixedPipelineState::DynamicState dynamic{key.state.dynamic_state};
static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
if (!key.state.dynamic_vertex_input) {
const size_t num_vertex_arrays = (std::min)(
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
for (size_t index = 0; index < num_vertex_arrays; ++index) {
const bool instanced = key.state.binding_divisors[index] != 0;
const auto rate =
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vertex_bindings.push_back({
const size_t num_vertex_arrays =
(std::min)(Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
for (size_t index = 0; index < num_vertex_arrays; ++index) {
const bool instanced = key.state.binding_divisors[index] != 0;
const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vertex_bindings.push_back({
.binding = static_cast<u32>(index),
.stride = key.state.vertex_strides[index],
.inputRate = rate,
});
if (instanced) {
vertex_binding_divisors.push_back({
.binding = static_cast<u32>(index),
.stride = key.state.vertex_strides[index],
.inputRate = rate,
});
if (instanced) {
vertex_binding_divisors.push_back({
.binding = static_cast<u32>(index),
.divisor = key.state.binding_divisors[index],
});
}
}
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
const auto& attribute = key.state.attributes[index];
if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) {
continue;
}
vertex_attributes.push_back({
.location = static_cast<u32>(index),
.binding = attribute.buffer,
.format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()),
.offset = attribute.offset,
.divisor = key.state.binding_divisors[index],
});
}
}
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
const auto& attribute = key.state.attributes[index];
if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) {
continue;
}
vertex_attributes.push_back({
.location = static_cast<u32>(index),
.binding = attribute.buffer,
.format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()),
.offset = attribute.offset,
});
}
ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
VkPipelineVertexInputStateCreateInfo vertex_input_ci{
@@ -629,12 +661,20 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (!vertex_binding_divisors.empty()) {
vertex_input_ci.pNext = &input_divisor_ci;
}
const bool has_tess_stages = spv_modules[1] || spv_modules[2];
auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
const bool has_tess_stages = static_cast<bool>(spv_modules[1]) || static_cast<bool>(spv_modules[2]);
const bool has_geometry_stage = static_cast<bool>(spv_modules[3]);
const bool dynamic_topology = key.state.extended_dynamic_state != 0;
const bool dynamic_primitive_restart = key.state.extended_dynamic_state_2 != 0;
const auto polygon_mode =
FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode);
const bool allow_polygon_mode_emulation =
!has_tess_stages && !has_geometry_stage && key.state.xfb_enabled == 0;
auto exact_input_assembly_topology = MaxwellToVK::PrimitiveTopology(
device, key.state.topology, polygon_mode, allow_polygon_mode_emulation);
if (exact_input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
if (!has_tess_stages) {
LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
exact_input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
}
} else {
if (has_tess_stages) {
@@ -642,25 +682,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
// shader stages. Forcing it fixes a crash on some drivers
LOG_WARNING(Render_Vulkan,
"Patch topology not used with tessellation, using patch list");
input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
exact_input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
}
}
const VkPrimitiveTopology input_assembly_topology =
dynamic_topology && dynamic_primitive_restart
? DynamicTopologyClassRepresentative(exact_input_assembly_topology)
: exact_input_assembly_topology;
const VkBool32 primitive_restart_enable =
// MoltenVK/Metal always has primitive restart enabled and cannot disable it
device.IsMoltenVK()
? VK_TRUE
: (dynamic_primitive_restart
? VK_FALSE
: (dynamic.primitive_restart_enable != 0 &&
SupportsStaticPrimitiveRestart(device, input_assembly_topology)
? VK_TRUE
: VK_FALSE));
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.topology = input_assembly_topology,
.primitiveRestartEnable =
// MoltenVK/Metal always has primitive restart enabled and cannot disable it
device.IsMoltenVK() ? VK_TRUE :
(dynamic.primitive_restart_enable != 0 &&
((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(input_assembly_topology) ||
(input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsPatchListPrimitiveRestartSupported()))
? VK_TRUE
: VK_FALSE),
.primitiveRestartEnable = primitive_restart_enable,
};
const VkPipelineTessellationStateCreateInfo tessellation_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -687,9 +731,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.viewportCount = num_viewports,
.viewportCount = key.state.extended_dynamic_state ? 0u : num_viewports,
.pViewports = nullptr,
.scissorCount = num_viewports,
.scissorCount = key.state.extended_dynamic_state ? 0u : num_viewports,
.pScissors = nullptr,
};
if (device.IsNvViewportSwizzleSupported()) {
@@ -716,20 +760,54 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
.lineWidth = 1.0f,
// TODO(alekpop): Transfer from regs
};
const bool smooth_lines_supported =
device.IsExtLineRasterizationSupported() && device.SupportsSmoothLines();
const bool stippled_lines_supported =
device.IsExtLineRasterizationSupported() && device.SupportsStippledRectangularLines();
const bool line_rasterization_supported = device.IsExtLineRasterizationSupported();
const bool any_stippled_lines_supported =
line_rasterization_supported &&
(device.SupportsStippledRectangularLines() || device.SupportsStippledBresenhamLines() ||
device.SupportsStippledSmoothLines());
const bool line_stipple_dynamic_state_supported =
IsLine(input_assembly_topology) && any_stippled_lines_supported;
const bool supports_rectangular_lines =
line_rasterization_supported && device.SupportsRectangularLines();
const bool supports_smooth_lines = line_rasterization_supported && device.SupportsSmoothLines();
VkLineRasterizationModeEXT line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT;
if (line_rasterization_supported) {
if (key.state.smooth_lines != 0) {
if (supports_smooth_lines) {
line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT;
} else if (supports_rectangular_lines) {
line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT;
}
} else {
if (supports_rectangular_lines) {
line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT;
} else if (supports_smooth_lines) {
line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT;
}
}
}
const bool stippled_lines_supported = [&]() {
if (!line_rasterization_supported || !dynamic.line_stipple_enable) {
return false;
}
switch (line_rasterization_mode) {
case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT:
return device.SupportsStippledRectangularLines();
case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT:
return device.SupportsStippledSmoothLines();
default:
return false;
}
}();
VkPipelineRasterizationLineStateCreateInfoEXT line_state{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT,
.pNext = nullptr,
.lineRasterizationMode = key.state.smooth_lines != 0 && smooth_lines_supported
? VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT
: VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT,
.stippledLineEnable =
(dynamic.line_stipple_enable && stippled_lines_supported) ? VK_TRUE : VK_FALSE,
.lineRasterizationMode = line_rasterization_mode,
.stippledLineEnable = stippled_lines_supported ? VK_TRUE : VK_FALSE,
.lineStippleFactor = key.state.line_stipple_factor,
.lineStipplePattern = static_cast<uint16_t>(key.state.line_stipple_pattern),
};
@@ -737,15 +815,31 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.conservativeRasterizationMode = key.state.conservative_raster_enable != 0
? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
.conservativeRasterizationMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
.extraPrimitiveOverestimationSize = 0.0f,
};
const bool conservative_requested = key.state.conservative_raster_enable != 0;
if (conservative_requested) {
const bool is_point_topology = input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
const bool is_line_topology = IsLine(input_assembly_topology);
const bool needs_point_or_line_support = is_point_topology || is_line_topology;
const bool supports_requested_topology =
!needs_point_or_line_support || device.SupportsConservativePointAndLineRasterization();
conservative_raster.conservativeRasterizationMode =
supports_requested_topology ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
}
const bool preserve_provoking_vertex_for_xfb =
!key.state.xfb_enabled || device.IsTransformFeedbackProvokingVertexPreserved();
const bool use_last_provoking_vertex =
key.state.provoking_vertex_last != 0 && preserve_provoking_vertex_for_xfb &&
device.IsProvokingVertexLastSupported();
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
.pNext = nullptr,
.provokingVertexMode = key.state.provoking_vertex_last != 0
.provokingVertexMode = use_last_provoking_vertex
? VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT
: VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT,
};
@@ -756,7 +850,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (device.IsExtConservativeRasterizationSupported()) {
conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster);
}
if (device.IsExtProvokingVertexSupported() && Settings::values.provoking_vertex.GetValue()) {
if (device.IsExtProvokingVertexSupported()) {
provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
}
@@ -804,13 +898,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
VK_COLOR_COMPONENT_A_BIT,
};
const auto& blend{key.state.attachments[index]};
const bool supports_blending =
!VideoCore::Surface::IsPixelFormatInteger(DecodeFormat(key.state.color_formats[index]));
const std::array mask{blend.Mask()};
VkColorComponentFlags write_mask{};
for (size_t i = 0; i < mask_table.size(); ++i) {
write_mask |= mask[i] ? mask_table[i] : 0;
}
cb_attachments.push_back({
.blendEnable = blend.enable != 0,
.blendEnable = supports_blending && blend.enable != 0,
.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()),
.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()),
.colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()),
@@ -830,17 +926,22 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
static_vector<VkDynamicState, 34> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
static_vector<VkDynamicState, 35> dynamic_states{
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_LINE_WIDTH,
};
if (line_stipple_dynamic_state_supported) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_EXT);
}
if (key.state.extended_dynamic_state) {
static constexpr std::array extended{
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT,
VK_DYNAMIC_STATE_CULL_MODE_EXT,
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
@@ -849,18 +950,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
// VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1
// Only use it if VIDS is not active (VIDS replaces it with full vertex input control)
if (!key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
}
}
// VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS
// Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
} else {
dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
}
// EDS2 - Core (3 states)
@@ -878,41 +970,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
// EDS3 - Blending (composite: 3 states)
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
// EDS3 - Enables (composite: per-feature)
if (key.state.extended_dynamic_state_3_enables) {
if (device.SupportsDynamicState3DepthClampEnable()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT);
}
if (device.SupportsDynamicState3LogicOpEnable()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT);
}
if (device.SupportsDynamicState3LineRasterizationMode()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT);
}
if (device.SupportsDynamicState3ConservativeRasterizationMode()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT);
}
if (device.SupportsDynamicState3LineStippleEnable()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT);
}
if (device.SupportsDynamicState3AlphaToCoverageEnable()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT);
}
if (device.SupportsDynamicState3AlphaToOneEnable()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT);
}
}
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -81,7 +81,6 @@ public:
const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos);
bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; }
bool SupportsAlphaToCoverage() const noexcept {
return fragment_has_color0_output;
}
@@ -93,6 +92,18 @@ public:
bool UsesExtendedDynamicState() const noexcept {
return key.state.extended_dynamic_state != 0;
}
bool UsesExtendedDynamicState2() const noexcept {
return key.state.extended_dynamic_state_2 != 0;
}
bool UsesExtendedDynamicState2LogicOp() const noexcept {
return key.state.extended_dynamic_state_2_logic_op != 0;
}
bool HasTessellationStages() const noexcept {
return static_cast<bool>(spv_modules[1]) || static_cast<bool>(spv_modules[2]);
}
bool HasGeometryStage() const noexcept {
return static_cast<bool>(spv_modules[3]);
}
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
@@ -58,7 +58,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 16;
constexpr u32 CACHE_VERSION = 17;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>
@@ -132,20 +132,6 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
return Shader::AttributeType::Float;
}
Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t index) {
switch (state.DynamicAttributeType(index)) {
case 0:
return Shader::AttributeType::Disabled;
case 1:
return Shader::AttributeType::Float;
case 2:
return Shader::AttributeType::SignedInt;
case 3:
return Shader::AttributeType::UnsignedInt;
}
return Shader::AttributeType::Disabled;
}
Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> programs,
const GraphicsPipelineCacheKey& key,
const Shader::IR::Program& program,
@@ -183,14 +169,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
}
info.convert_depth_mode = gl_ndc;
}
if (key.state.dynamic_vertex_input) {
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
info.generic_input_types[index] = AttributeType(key.state, index);
}
} else {
std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
&CastAttributeType);
}
std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
&CastAttributeType);
break;
case Shader::Stage::TessellationEval:
info.tess_clockwise = key.state.tessellation_clockwise != 0;
@@ -269,19 +249,28 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
dst_a == F::Source1Alpha_GL || dst_a == F::OneMinusSource1Alpha_GL;
}
if (device.IsMoltenVK()) {
for (size_t i = 0; i < 8; ++i) {
const auto format = static_cast<Tegra::RenderTargetFormat>(key.state.color_formats[i]);
const auto pixel_format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(format);
if (VideoCore::Surface::IsPixelFormatInteger(pixel_format)) {
if (VideoCore::Surface::IsPixelFormatSignedInteger(pixel_format)) {
info.color_output_types[i] = Shader::AttributeType::SignedInt;
} else {
info.color_output_types[i] = Shader::AttributeType::UnsignedInt;
}
for (size_t i = 0; i < info.active_color_outputs.size(); ++i) {
const auto format = static_cast<Tegra::RenderTargetFormat>(key.state.color_formats[i]);
info.active_color_outputs[i] = format != Tegra::RenderTargetFormat::NONE;
}
if (info.dual_source_blend && info.active_color_outputs[0]) {
info.active_color_outputs[1] = true;
}
if (info.alpha_test_func && *info.alpha_test_func != Shader::CompareFunction::Always) {
info.active_color_outputs[0] = true;
}
for (size_t i = 0; i < 8; ++i) {
const auto format = static_cast<Tegra::RenderTargetFormat>(key.state.color_formats[i]);
const auto pixel_format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(format);
if (VideoCore::Surface::IsPixelFormatInteger(pixel_format)) {
if (VideoCore::Surface::IsPixelFormatSignedInteger(pixel_format)) {
info.color_output_types[i] = Shader::AttributeType::SignedInt;
} else {
info.color_output_types[i] = Shader::AttributeType::Float;
info.color_output_types[i] = Shader::AttributeType::UnsignedInt;
}
} else {
info.color_output_types[i] = Shader::AttributeType::Float;
}
}
break;
@@ -469,7 +458,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
// Level 0: Core Dynamic States only
// Level 1: Core + EDS1
// Level 2: Core + EDS1 + EDS2 (accumulative)
// Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative)
// Level 2: Core + EDS1 + EDS2 (accumulative)
// Here we only verify if extensions were successfully loaded by the device
dynamic_features.has_extended_dynamic_state =
@@ -480,16 +469,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
dynamic_features.has_extended_dynamic_state_2_logic_op =
device.IsExtExtendedDynamicState2ExtrasSupported();
dynamic_features.has_extended_dynamic_state_2_patch_control_points = false;
dynamic_features.has_extended_dynamic_state_3_blend =
device.IsExtExtendedDynamicState3BlendingSupported();
dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported();
// VIDS: Independent toggle (not affected by dyna_state levels)
dynamic_features.has_dynamic_vertex_input =
device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue();
}
PipelineCache::~PipelineCache() {
@@ -595,12 +574,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
(key.state.extended_dynamic_state_2 != 0) !=
dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_logic_op != 0) !=
dynamic_features.has_extended_dynamic_state_2_logic_op ||
(key.state.extended_dynamic_state_3_blend != 0) !=
dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) !=
dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
dynamic_features.has_extended_dynamic_state_2_logic_op) {
return;
}
workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable {
+167 -62
View File
@@ -145,6 +145,7 @@ public:
scheduler.Record([buffer = *accumulation_buffer](vk::CommandBuffer cmdbuf) {
cmdbuf.FillBuffer(buffer, 0, 8, 0);
});
ReserveBank();
}
~SamplesStreamer() = default;
@@ -202,6 +203,11 @@ public:
}
void SyncWrites() override {
if (!direct_sync_values.empty()) {
runtime.template SyncValues<VideoCommon::SyncValuesStruct>(direct_sync_values);
direct_sync_values.clear();
}
if (sync_values_stash.empty()) {
return;
}
@@ -220,10 +226,55 @@ public:
}
PauseCounter();
const auto driver_id = device.GetDriverID();
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
pending_sync.clear();
if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) {
ApplyBanksWideOp<false>(
pending_sync,
[](SamplesQueryBank* bank, size_t start, size_t amount) { bank->Sync(start, amount); });
direct_sync_values.clear();
direct_sync_values.reserve(pending_sync.size());
bool has_multi_queries = accumulation_since_last_sync;
for (auto q : pending_sync) {
auto* query = GetQuery(q);
if (True(query->flags & VideoCommon::QueryFlagBits::IsRewritten)) {
continue;
}
if (True(query->flags & VideoCommon::QueryFlagBits::IsInvalidated)) {
continue;
}
u64 total = 0;
ApplyBankOp(query, [&total](SamplesQueryBank* bank, size_t start, size_t amount) {
const auto& results = bank->GetResults();
for (size_t i = 0; i < amount; i++) {
total += results[start + i];
}
});
const u64 final_total = total + GetAmendValue();
query->value = total;
query->flags |= VideoCommon::QueryFlagBits::IsHostSynced;
query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
direct_sync_values.emplace_back(VideoCommon::SyncValuesStruct{
.address = query->guest_address,
.value = final_total,
.size = SamplesQueryBank::QUERY_SIZE,
});
has_multi_queries |= query->size_slots > 1;
}
ReplicateCurrentQueryIfNeeded();
std::function<void()> func([this] { amend_value = accumulation_value; });
rasterizer->SyncOperation(std::move(func));
AbandonCurrentQuery();
num_slots_used = 0;
first_accumulation_checkpoint = (std::numeric_limits<size_t>::max)();
last_accumulation_checkpoint = 0;
accumulation_since_last_sync = has_multi_queries;
sync_values_stash.clear();
pending_sync.clear();
return;
}
sync_values_stash.clear();
@@ -569,6 +620,7 @@ private:
std::array<size_t, 32> resolve_table{};
std::array<size_t, 32> intermediary_table{};
vk::Buffer accumulation_buffer;
std::vector<VideoCommon::SyncValuesStruct> direct_sync_values;
std::deque<std::vector<HostSyncValues>> sync_values_stash;
std::vector<size_t> resolve_buffers;
@@ -580,16 +632,16 @@ private:
std::deque<std::vector<size_t>> pending_flush_sets;
// State Machine
size_t current_bank_slot;
size_t current_bank_id;
SamplesQueryBank* current_bank;
VkQueryPool current_query_pool;
size_t current_query_id;
size_t current_bank_slot{};
size_t current_bank_id{};
SamplesQueryBank* current_bank{};
VkQueryPool current_query_pool{};
size_t current_query_id{};
size_t num_slots_used{};
size_t first_accumulation_checkpoint{};
size_t last_accumulation_checkpoint{};
bool accumulation_since_last_sync{};
VideoCommon::HostQueryBase* current_query;
VideoCommon::HostQueryBase* current_query{};
bool has_started{};
std::mutex flush_guard;
@@ -665,7 +717,7 @@ public:
counter_buffers.fill(VK_NULL_HANDLE);
offsets.fill(0);
last_queries.fill(0);
last_queries_stride.fill(1);
last_queries_stride.fill(0);
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -861,14 +913,16 @@ private:
has_flushed_end_pending = true;
// Refresh buffers state before beginning transform feedback so counters are up-to-date
UpdateBuffers();
if (!has_started || buffers_count == 0) {
active_buffers_count = has_started ? buffers_count : 0;
if (active_buffers_count == 0) {
// No counter buffers available: begin without counters
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr);
});
return;
}
scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) {
scheduler.Record(
[this, total = static_cast<u32>(active_buffers_count)](vk::CommandBuffer cmdbuf) {
cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data());
});
}
@@ -879,30 +933,31 @@ private:
return;
}
has_flushed_end_pending = false;
// Refresh buffer state before ending transform feedback to ensure counters_count is up-to-date.
UpdateBuffers();
if (buffers_count == 0) {
LOG_DEBUG(Render_Vulkan, "EndTransformFeedbackEXT called with no counters (buffers_count=0)");
if (active_buffers_count == 0) {
LOG_DEBUG(Render_Vulkan,
"EndTransformFeedbackEXT called with no counters (active_buffers_count=0)");
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr);
});
} else {
LOG_DEBUG(Render_Vulkan, "EndTransformFeedbackEXT called with counters (buffers_count={})", buffers_count);
LOG_DEBUG(Render_Vulkan,
"EndTransformFeedbackEXT called with counters (active_buffers_count={})",
active_buffers_count);
scheduler.Record([this,
total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) {
total = static_cast<u32>(active_buffers_count)](vk::CommandBuffer cmdbuf) {
cmdbuf.EndTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data());
});
}
active_buffers_count = 0;
}
void UpdateBuffers() {
last_queries.fill(0);
last_queries_stride.fill(1);
last_queries_stride.fill(0);
streams_mask = 0; // reset previously recorded streams
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0;
out_topology = maxwell3d.draw_manager->GetDrawState().topology;
out_topology = maxwell3d.GetTransformFeedbackOutputTopology();
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
@@ -913,7 +968,9 @@ private:
LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream);
continue;
}
last_queries_stride[stream] = tf.controls[i].stride;
if (last_queries_stride[stream] == 0) {
last_queries_stride[stream] = tf.controls[i].stride;
}
streams_mask |= 1ULL << stream;
buffers_count = std::max<size_t>(buffers_count, stream + 1);
}
@@ -990,6 +1047,7 @@ private:
bool has_started{};
bool has_flushed_end_pending{};
size_t buffers_count{};
size_t active_buffers_count{};
std::array<VkBuffer, NUM_STREAMS> counter_buffers{};
std::array<VkDeviceSize, NUM_STREAMS> offsets{};
std::array<DAddr, NUM_STREAMS> last_queries;
@@ -1015,10 +1073,41 @@ public:
u64 stride{};
DAddr dependant_address{};
Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points};
u32 patch_vertices{1};
size_t dependant_index{};
bool dependant_manage{};
};
[[nodiscard]] constexpr u64 PrimitiveCountFromVertices(
Maxwell3D::Regs::PrimitiveTopology topology, u64 num_vertices, u32 patch_vertices) {
switch (topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points:
return num_vertices;
case Maxwell3D::Regs::PrimitiveTopology::Lines:
return num_vertices / 2;
case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::Triangles:
return num_vertices / 3;
case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 6;
case Maxwell3D::Regs::PrimitiveTopology::Patches:
return patch_vertices != 0 ? num_vertices / patch_vertices : 0;
case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
case Maxwell3D::Regs::PrimitiveTopology::Quads:
case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
case Maxwell3D::Regs::PrimitiveTopology::Polygon:
return 0;
default:
return num_vertices;
}
}
class PrimitivesSucceededStreamer : public VideoCommon::SimpleStreamer<PrimitivesQueryBase> {
public:
explicit PrimitivesSucceededStreamer(size_t id_, QueryCacheRuntime& runtime_,
@@ -1047,7 +1136,10 @@ public:
const size_t subreport = static_cast<size_t>(*subreport_);
auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport);
bool must_manage_dependance = false;
new_query->topology = tfb_streamer.GetOutputTopology();
runtime.View3DRegs([new_query, this](Maxwell3D& maxwell3d) {
new_query->topology = tfb_streamer.GetOutputTopology();
new_query->patch_vertices = std::max<u32>(maxwell3d.regs.patch_vertices, 1);
});
if (dependant_address_opt) {
auto [dep_address, stride] = *dependant_address_opt;
new_query->dependant_address = dep_address;
@@ -1066,7 +1158,7 @@ public:
}
return index;
}
new_query->stride = 1;
new_query->stride = 0;
runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) {
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
@@ -1130,32 +1222,8 @@ public:
num_vertices = static_cast<u64>(result) / safe_stride;
}
}
query->value = [&]() -> u64 {
switch (query->topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points:
return num_vertices;
case Maxwell3D::Regs::PrimitiveTopology::Lines:
return num_vertices / 2;
case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
return (num_vertices / 2) + 1;
case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
return num_vertices - 1;
case Maxwell3D::Regs::PrimitiveTopology::Patches:
case Maxwell3D::Regs::PrimitiveTopology::Triangles:
case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 3;
case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
return num_vertices - 2;
case Maxwell3D::Regs::PrimitiveTopology::Quads:
return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::Polygon:
return 1U;
default:
return num_vertices;
}
}();
query->value =
PrimitiveCountFromVertices(query->topology, num_vertices, query->patch_vertices);
}
}
@@ -1205,13 +1273,18 @@ struct QueryCacheRuntimeImpl {
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
VkBufferUsageFlags conditional_usage =
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (device.IsExtConditionalRendering()) {
conditional_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
}
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = sizeof(u32),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
.usage = conditional_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -1307,6 +1380,35 @@ void QueryCacheRuntime::ResumeHostConditionalRendering() {
impl->is_hcr_running = true;
}
void QueryCacheRuntime::SetHostConditionalRenderingResult(bool condition_passed) {
if (!impl->device.IsExtConditionalRendering()) {
return;
}
const bool was_running = impl->is_hcr_running;
if (was_running) {
PauseHostConditionalRendering();
}
impl->scheduler.RequestOutsideRenderPassOperationContext();
impl->scheduler.Record(
[buffer = *impl->hcr_resolve_buffer, value = condition_passed ? 1u : 0u](vk::CommandBuffer cmdbuf) {
cmdbuf.FillBuffer(buffer, 0, sizeof(u32), value);
});
impl->hcr_setup.buffer = *impl->hcr_resolve_buffer;
impl->hcr_setup.offset = 0;
impl->hcr_setup.flags = 0;
impl->hcr_buffer = *impl->hcr_resolve_buffer;
impl->hcr_offset = 0;
impl->hcr_is_set = true;
impl->is_hcr_running = false;
if (was_running) {
ResumeHostConditionalRendering();
}
}
void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object,
bool is_equal) {
{
@@ -1417,13 +1519,6 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
return false;
}
auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh();
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
return true;
}
for (size_t i = 0; i < 2; i++) {
is_null[i] = !is_in_ac[i] && check_value(objects[i]->address);
}
@@ -1436,12 +1531,22 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
}
}
if (!is_gpu_high) {
return true;
auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh();
const bool driver_blocks_pair_resolve =
((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) ||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP);
if (driver_blocks_pair_resolve || !is_gpu_high) {
EndHostConditionalRendering();
return false;
}
if (!is_in_bc[0] && !is_in_bc[1]) {
return true;
EndHostConditionalRendering();
return false;
}
HostConditionalRenderingCompareBCImpl(object_1.address, equal_check);
return true;
@@ -48,6 +48,8 @@ public:
void ResumeHostConditionalRendering();
void SetHostConditionalRenderingResult(bool condition_passed);
bool HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1, bool qc_dirty);
bool HostConditionalRenderingCompareValues(VideoCommon::LookupData object_1,
File diff suppressed because it is too large Load Diff
+15 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -122,6 +122,7 @@ public:
void FlushCommands() override;
void TickFrame() override;
bool AccelerateConditionalRendering() override;
void SetConditionalRenderingPredicate(bool enabled) override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -155,6 +156,17 @@ private:
template <typename Func>
void PrepareDraw(bool is_indexed, Func&&);
bool EmulateLineLoopDraw(const Tegra::Engines::DrawManager::State& draw_state,
u32 base_instance, u32 num_instances, s32 base_vertex,
u32 num_vertices, u32 first_index, bool is_indexed);
bool EmulateIndirectLineLoopDraw(const Tegra::Engines::DrawManager::State& draw_state,
const Tegra::Engines::DrawManager::IndirectParams& params);
bool DrawLineLoopClosure(const Tegra::Engines::DrawManager::State& draw_state,
u32 base_instance, u32 num_instances, s32 base_vertex,
u32 num_vertices, u32 first_index, bool is_indexed);
void DrawIndirectLineLoopClosures(const Tegra::Engines::DrawManager::State& draw_state,
const Tegra::Engines::DrawManager::IndirectParams& params);
void FlushWork();
void UpdateDynamicStates();
@@ -168,23 +180,17 @@ private:
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
@@ -154,9 +154,11 @@ void Scheduler::RequestOutsideRenderPassOperationContext() {
bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
if (state.graphics_pipeline == pipeline) {
if (pipeline && pipeline->UsesExtendedDynamicState() &&
if (pipeline &&
(pipeline->UsesExtendedDynamicState() || pipeline->UsesExtendedDynamicState2() ||
pipeline->UsesExtendedDynamicState2LogicOp()) &&
state.needs_state_enable_refresh) {
state_tracker.InvalidateStateEnableFlag();
state_tracker.InvalidateExtendedDynamicStates();
state.needs_state_enable_refresh = false;
}
return false;
@@ -168,10 +170,16 @@ bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
return true;
}
if (!pipeline->UsesExtendedDynamicState()) {
if (pipeline->UsesExtendedDynamicState() || pipeline->UsesExtendedDynamicState2() ||
pipeline->UsesExtendedDynamicState2LogicOp()) {
state_tracker.InvalidateExtendedDynamicStates();
}
if (!pipeline->UsesExtendedDynamicState() && !pipeline->UsesExtendedDynamicState2() &&
!pipeline->UsesExtendedDynamicState2LogicOp()) {
state.needs_state_enable_refresh = true;
} else if (state.needs_state_enable_refresh) {
state_tracker.InvalidateStateEnableFlag();
state_tracker.InvalidateExtendedDynamicStates();
state.needs_state_enable_refresh = false;
}
@@ -337,6 +345,13 @@ void Scheduler::EndRenderPass()
images = renderpass_images,
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
std::array<VkImageMemoryBarrier, 9> barriers;
constexpr VkPipelineStageFlags src_stages =
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
constexpr VkPipelineStageFlags dst_stages =
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
for (size_t i = 0; i < num_images; ++i) {
const VkImageSubresourceRange& range = ranges[i];
const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
@@ -372,9 +387,8 @@ void Scheduler::EndRenderPass()
};
}
cmdbuf.EndRenderPass();
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_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
cmdbuf.PipelineBarrier(src_stages, dst_stages, 0, nullptr, nullptr,
vk::Span(barriers.data(), num_images));
});
state.renderpass = VkRenderPass{};
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -40,6 +40,7 @@ Flags MakeInvalidationFlags() {
StencilWriteMask,
StencilCompare,
LineWidth,
LineStipple,
CullMode,
DepthBoundsEnable,
DepthTestEnable,
@@ -54,19 +55,11 @@ Flags MakeInvalidationFlags() {
StateEnable,
PrimitiveRestartEnable,
DepthBiasEnable,
LogicOpEnable,
DepthClampEnable,
AlphaToCoverageEnable,
AlphaToOneEnable,
LineRasterizationMode,
LogicOp,
Blending,
ColorMask,
BlendEquations,
BlendEnable,
ConservativeRasterizationMode,
LineStippleEnable,
LineStippleParams,
};
Flags flags{};
for (const int flag : INVALIDATION_FLAGS) {
@@ -127,6 +120,13 @@ void SetupDirtyStencilProperties(Tables& tables) {
void SetupDirtyLineWidth(Tables& tables) {
tables[0][OFF(line_width_smooth)] = LineWidth;
tables[0][OFF(line_width_aliased)] = LineWidth;
tables[0][OFF(line_anti_alias_enable)] = LineWidth;
}
void SetupDirtyLineStipple(Tables& tables) {
tables[0][OFF(line_stipple_enable)] = LineStipple;
FillBlock(tables[0], OFF(line_stipple_params), NUM(line_stipple_params), LineStipple);
tables[1][OFF(line_anti_alias_enable)] = LineStipple;
}
void SetupDirtyCullMode(Tables& tables) {
@@ -149,11 +149,6 @@ void SetupDirtyStateEnable(Tables& tables) {
setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);
setup(OFF(logic_op.enable), LogicOpEnable);
setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable);
setup(OFF(line_stipple_enable), LineStippleEnable);
setup(OFF(anti_alias_alpha_control.alpha_to_coverage), AlphaToCoverageEnable);
setup(OFF(anti_alias_alpha_control.alpha_to_one), AlphaToOneEnable);
}
void SetupDirtyDepthCompareOp(Tables& tables) {
@@ -227,13 +222,6 @@ void SetupDirtyVertexBindings(Tables& tables) {
}
}
void SetupRasterModes(Tables &tables) {
auto& table = tables[0];
table[OFF(line_stipple_params)] = LineStippleParams;
table[OFF(conservative_raster_enable)] = ConservativeRasterizationMode;
table[OFF(line_anti_alias_enable)] = LineRasterizationMode;
}
} // Anonymous namespace
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
@@ -246,6 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
SetupDirtyDepthBounds(tables);
SetupDirtyStencilProperties(tables);
SetupDirtyLineWidth(tables);
SetupDirtyLineStipple(tables);
SetupDirtyCullMode(tables);
SetupDirtyStateEnable(tables);
SetupDirtyDepthCompareOp(tables);
@@ -256,7 +245,6 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
SetupDirtyVertexAttributes(tables);
SetupDirtyVertexBindings(tables);
SetupDirtySpecialOps(tables);
SetupRasterModes(tables);
}
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -42,6 +42,7 @@ enum : u8 {
StencilWriteMask,
StencilCompare,
LineWidth,
LineStipple,
CullMode,
DepthBoundsEnable,
@@ -53,17 +54,9 @@ enum : u8 {
StencilTestEnable,
PrimitiveRestartEnable,
RasterizerDiscardEnable,
ConservativeRasterizationMode,
LineRasterizationMode,
LineStippleEnable,
LineStippleParams,
DepthBiasEnable,
StateEnable,
LogicOp,
LogicOpEnable,
DepthClampEnable,
AlphaToCoverageEnable,
AlphaToOneEnable,
Blending,
BlendEnable,
@@ -101,6 +94,25 @@ public:
(*flags)[Dirty::StateEnable] = true;
}
void InvalidateExtendedDynamicStates() {
(*flags)[Dirty::Viewports] = true;
(*flags)[Dirty::Scissors] = true;
(*flags)[Dirty::CullMode] = true;
(*flags)[Dirty::DepthBoundsEnable] = true;
(*flags)[Dirty::DepthTestEnable] = true;
(*flags)[Dirty::DepthWriteEnable] = true;
(*flags)[Dirty::DepthCompareOp] = true;
(*flags)[Dirty::FrontFace] = true;
(*flags)[Dirty::StencilOp] = true;
(*flags)[Dirty::StencilTestEnable] = true;
(*flags)[Dirty::StateEnable] = true;
(*flags)[Dirty::PrimitiveRestartEnable] = true;
(*flags)[Dirty::RasterizerDiscardEnable] = true;
(*flags)[Dirty::DepthBiasEnable] = true;
(*flags)[Dirty::LogicOp] = true;
current_topology = INVALID_TOPOLOGY;
}
bool TouchViewports() {
const bool dirty_viewports = Exchange(Dirty::Viewports, false);
const bool rescale_viewports = Exchange(VideoCommon::Dirty::RescaleViewports, false);
@@ -177,6 +189,10 @@ public:
return ExchangeCheck(back.compare_mask, new_value) || stencil_reset;
}
void ResetStencilState() {
stencil_reset = true;
}
void ClearStencilReset() {
stencil_reset = false;
}
@@ -185,6 +201,10 @@ public:
return Exchange(Dirty::LineWidth, false);
}
bool TouchLineStipple() const {
return Exchange(Dirty::LineStipple, false);
}
bool TouchCullMode() {
return Exchange(Dirty::CullMode, false);
}
@@ -213,33 +233,8 @@ public:
return Exchange(Dirty::RasterizerDiscardEnable, false);
}
bool TouchConservativeRasterizationMode()
{
return Exchange(Dirty::ConservativeRasterizationMode, false);
}
bool TouchLineStippleEnable() { return Exchange(Dirty::LineStippleEnable, false); }
bool TouchLineStipple() { return Exchange(Dirty::LineStippleParams, false); }
bool TouchDepthBiasEnable() { return Exchange(Dirty::DepthBiasEnable, false); }
bool TouchLogicOpEnable() {
return Exchange(Dirty::LogicOpEnable, false);
}
bool TouchDepthClampEnable() {
return Exchange(Dirty::DepthClampEnable, false);
}
bool TouchAlphaToCoverageEnable() {
return Exchange(Dirty::AlphaToCoverageEnable, false);
}
bool TouchAlphaToOneEnable() {
return Exchange(Dirty::AlphaToOneEnable, false);
}
bool TouchDepthCompareOp() {
return Exchange(Dirty::DepthCompareOp, false);
}
@@ -276,11 +271,7 @@ public:
return Exchange(Dirty::LogicOp, false);
}
bool TouchLineRasterizationMode() {
return Exchange(Dirty::LineRasterizationMode, false);
}
bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
bool ChangePrimitiveTopology(u32 new_topology) {
const bool has_changed = current_topology != new_topology;
current_topology = new_topology;
return has_changed;
@@ -293,7 +284,7 @@ public:
void InvalidateState();
private:
static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
static constexpr u32 INVALID_TOPOLOGY = ~0u;
bool Exchange(std::size_t id, bool new_value) const noexcept {
const bool is_dirty = (*flags)[id];
@@ -310,7 +301,7 @@ private:
Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags;
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
u32 current_topology = INVALID_TOPOLOGY;
bool two_sided_stencil = false;
StencilProperties front{};
StencilProperties back{};
@@ -9,6 +9,10 @@
#include <limits>
#include <vector>
#ifdef __ANDROID__
#include <android/api-level.h>
#endif
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
@@ -170,6 +174,7 @@ bool Swapchain::AcquireNextImage() {
break;
}
const auto wait_with_frame_pacing = [this] {
switch (Settings::values.frame_pacing_mode.GetValue()) {
case Settings::FramePacingMode::Target_Auto:
scheduler.Wait(resource_ticks[image_index]);
@@ -187,6 +192,17 @@ bool Swapchain::AcquireNextImage() {
scheduler.Wait(resource_ticks[image_index], 120.0);
break;
}
};
#ifdef __ANDROID__
if (android_get_device_api_level() >= 30) {
scheduler.Wait(resource_ticks[image_index]);
} else {
wait_with_frame_pacing();
}
#else
wait_with_frame_pacing();
#endif
resource_ticks[image_index] = scheduler.CurrentTick();
@@ -446,15 +446,24 @@ TransformBufferCopies(std::span<const VideoCommon::BufferCopy> copies, size_t bu
};
}
[[nodiscard]] VkImageSubresourceRange MakeBarrierSubresourceRange(
VkImageAspectFlags aspect_mask, const SubresourceRange& range, bool is_3d_image) {
VkImageSubresourceRange subresource_range = MakeSubresourceRange(aspect_mask, range);
if (is_3d_image && subresource_range.layerCount == 1) {
subresource_range.layerCount = VK_REMAINING_ARRAY_LAYERS;
}
return subresource_range;
}
[[nodiscard]] VkImageSubresourceRange MakeSubresourceRange(const ImageView* image_view) {
SubresourceRange range = image_view->range;
const bool is_3d_image = image_view->type == VideoCommon::ImageViewType::e3D ||
True(image_view->flags & VideoCommon::ImageViewFlagBits::Slice);
if (True(image_view->flags & VideoCommon::ImageViewFlagBits::Slice)) {
// Slice image views always affect a single layer, but their subresource range corresponds
// to the slice. Override the value to affect a single layer.
range.base.layer = 0;
range.extent.layers = 1;
}
return MakeSubresourceRange(ImageAspectMask(image_view->format), range);
return MakeBarrierSubresourceRange(ImageAspectMask(image_view->format), range, is_3d_image);
}
[[nodiscard]] VkImageSubresourceLayers MakeSubresourceLayers(const ImageView* image_view) {
@@ -524,18 +533,23 @@ struct RangedBarrierRange {
max_layer = (std::max)(max_layer, layers.baseArrayLayer + layers.layerCount);
}
VkImageSubresourceRange SubresourceRange(VkImageAspectFlags aspect_mask) const noexcept {
return VkImageSubresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = min_mip,
.levelCount = max_mip - min_mip,
.baseArrayLayer = min_layer,
.layerCount = max_layer - min_layer,
VkImageSubresourceRange SubresourceRange(VkImageAspectFlags aspect_mask,
bool is_3d_image) const noexcept {
const VideoCommon::SubresourceRange range{
.base = {
.level = static_cast<s32>(min_mip),
.layer = static_cast<s32>(min_layer),
},
.extent = {
.levels = static_cast<s32>(max_mip - min_mip),
.layers = static_cast<s32>(max_layer - min_layer),
},
};
return MakeBarrierSubresourceRange(aspect_mask, range, is_3d_image);
}
};
void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
VkImageAspectFlags aspect_mask, bool is_initialized,
VkImageAspectFlags aspect_mask, bool is_initialized, bool is_3d_image,
std::span<const VkBufferImageCopy> copies) {
static constexpr VkAccessFlags WRITE_ACCESS_FLAGS =
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
@@ -549,7 +563,8 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
for (const auto& region : copies) {
range.AddLayers(region.imageSubresource);
}
const VkImageSubresourceRange subresource_range = range.SubresourceRange(aspect_mask);
const VkImageSubresourceRange subresource_range =
range.SubresourceRange(aspect_mask, is_3d_image);
const VkImageMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@@ -1006,9 +1021,12 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
const VkBuffer copy_buffer = GetTemporaryBuffer(total_size);
const VkImage dst_image = dst.Handle();
const VkImage src_image = src.Handle();
const bool dst_is_3d = dst.info.type == ImageType::e3D;
const bool src_is_3d = src.info.type == ImageType::e3D;
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_image, src_image, copy_buffer, src_aspect_mask, dst_aspect_mask,
vk_in_copies, vk_out_copies](vk::CommandBuffer cmdbuf) {
dst_is_3d, src_is_3d, vk_in_copies,
vk_out_copies](vk::CommandBuffer cmdbuf) {
RangedBarrierRange dst_range;
RangedBarrierRange src_range;
for (const VkBufferImageCopy& copy : vk_in_copies) {
@@ -1042,7 +1060,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src_image,
.subresourceRange = src_range.SubresourceRange(src_aspect_mask),
.subresourceRange = src_range.SubresourceRange(src_aspect_mask, src_is_3d),
},
};
const std::array middle_in_barrier{
@@ -1056,7 +1074,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src_image,
.subresourceRange = src_range.SubresourceRange(src_aspect_mask),
.subresourceRange = src_range.SubresourceRange(src_aspect_mask, src_is_3d),
},
};
const std::array middle_out_barrier{
@@ -1072,7 +1090,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange = dst_range.SubresourceRange(dst_aspect_mask),
.subresourceRange = dst_range.SubresourceRange(dst_aspect_mask, dst_is_3d),
},
};
const std::array post_barriers{
@@ -1091,7 +1109,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange = dst_range.SubresourceRange(dst_aspect_mask),
.subresourceRange = dst_range.SubresourceRange(dst_aspect_mask, dst_is_3d),
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
@@ -1440,6 +1458,8 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size());
const VkImageAspectFlags aspect_mask = dst.AspectMask();
ASSERT(aspect_mask == src.AspectMask());
const bool dst_is_3d = dst.info.type == ImageType::e3D;
const bool src_is_3d = src.info.type == ImageType::e3D;
std::ranges::transform(copies, vk_copies.begin(), [aspect_mask](const auto& copy) {
return MakeImageCopy(copy, aspect_mask);
@@ -1447,7 +1467,8 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
const VkImage dst_image = dst.Handle();
const VkImage src_image = src.Handle();
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_image, src_image, aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) {
scheduler.Record([dst_image, src_image, aspect_mask, dst_is_3d, src_is_3d,
vk_copies](vk::CommandBuffer cmdbuf) {
RangedBarrierRange dst_range;
RangedBarrierRange src_range;
for (const VkImageCopy& copy : vk_copies) {
@@ -1467,7 +1488,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src_image,
.subresourceRange = src_range.SubresourceRange(aspect_mask),
.subresourceRange = src_range.SubresourceRange(aspect_mask, src_is_3d),
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@@ -1481,7 +1502,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange = dst_range.SubresourceRange(aspect_mask),
.subresourceRange = dst_range.SubresourceRange(aspect_mask, dst_is_3d),
},
};
const std::array post_barriers{
@@ -1495,7 +1516,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src_image,
.subresourceRange = src_range.SubresourceRange(aspect_mask),
.subresourceRange = src_range.SubresourceRange(aspect_mask, src_is_3d),
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@@ -1512,7 +1533,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange = dst_range.SubresourceRange(aspect_mask),
.subresourceRange = dst_range.SubresourceRange(aspect_mask, dst_is_3d),
},
};
cmdbuf.PipelineBarrier(
@@ -1691,10 +1712,12 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
const VkBuffer src_buffer = buffer;
const VkImage temp_vk_image = *temp_wrapper->original_image;
const VkImageAspectFlags vk_aspect_mask = temp_wrapper->aspect_mask;
const bool temp_is_3d = temp_info.type == ImageType::e3D;
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies,
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, temp_is_3d, vk_copies,
keep = temp_wrapper](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies));
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false,
temp_is_3d, VideoCommon::FixSmallVectorADL(vk_copies));
});
// Use MSAACopyPass to convert from non-MSAA to MSAA
@@ -1730,10 +1753,12 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
const VkImage vk_image = *original_image;
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
const bool was_initialized = std::exchange(initialized, true);
const bool is_3d_image = info.type == ImageType::e3D;
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized, is_3d_image,
vk_copies](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, VideoCommon::FixSmallVectorADL(vk_copies));
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized,
is_3d_image, VideoCommon::FixSmallVectorADL(vk_copies));
});
if (is_rescaled) {
@@ -2112,7 +2137,10 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
const VkImageUsageFlags desired_view_usage = ImageUsageFlags(format_info, format);
const VkImageUsageFlags image_usage = image.UsageFlags();
const VkImageUsageFlags view_usage = desired_view_usage & image_usage;
if (desired_view_usage != image_usage) {
LOG_WARNING(Render_Vulkan,
"Image view format {} has different usage flags than image format {}", format,
image.info.format);
@@ -2120,7 +2148,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
const VkImageViewUsageCreateInfo image_view_usage{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.pNext = nullptr,
.usage = ImageUsageFlags(format_info, format),
.usage = view_usage,
};
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -2286,6 +2314,7 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
const auto& device = runtime.device;
has_depth_compare = tsc.depth_compare_enabled != 0;
// Check if custom border colors are supported
const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported();
const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported();
@@ -2326,7 +2355,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
// Some games have samplers with garbage. Sanitize them here.
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
const auto create_sampler = [&](const f32 anisotropy) {
const auto create_sampler = [&](const f32 anisotropy, bool enable_depth_compare) {
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = pnext,
@@ -2340,7 +2369,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
.mipLodBias = tsc.LodBias(),
.anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
.maxAnisotropy = anisotropy,
.compareEnable = tsc.depth_compare_enabled,
.compareEnable = enable_depth_compare,
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
@@ -2350,11 +2379,18 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
});
};
sampler = create_sampler(max_anisotropy);
sampler = create_sampler(max_anisotropy, has_depth_compare);
if (has_depth_compare) {
sampler_no_compare = create_sampler(max_anisotropy, false);
}
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
if (max_anisotropy > max_anisotropy_default) {
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
sampler_default_anisotropy = create_sampler(max_anisotropy_default, has_depth_compare);
if (has_depth_compare) {
sampler_default_anisotropy_no_compare =
create_sampler(max_anisotropy_default, false);
}
}
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -396,11 +396,18 @@ class Sampler {
public:
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
[[nodiscard]] VkSampler Handle() const noexcept {
[[nodiscard]] VkSampler Handle(bool enable_depth_compare = true) const noexcept {
if (!enable_depth_compare && sampler_no_compare) {
return *sampler_no_compare;
}
return *sampler;
}
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy(
bool enable_depth_compare = true) const noexcept {
if (!enable_depth_compare && sampler_default_anisotropy_no_compare) {
return *sampler_default_anisotropy_no_compare;
}
return *sampler_default_anisotropy;
}
@@ -408,9 +415,16 @@ public:
return static_cast<bool>(sampler_default_anisotropy);
}
[[nodiscard]] bool HasDepthCompareEnabled() const noexcept {
return has_depth_compare;
}
private:
vk::Sampler sampler;
vk::Sampler sampler_no_compare;
vk::Sampler sampler_default_anisotropy;
vk::Sampler sampler_default_anisotropy_no_compare;
bool has_depth_compare = false;
};
struct TextureCacheParams {
+1 -1
View File
@@ -1191,7 +1191,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i
template <class P>
void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
if (False(image.flags & ImageFlagBits::CpuModified)) {
// Only upload modified images
runtime.TransitionImageLayout(image);
return;
}
+1 -4
View File
@@ -22,16 +22,13 @@
#include <vulkan/vulkan.h>
// Define maintenance 7-9 extension names (not yet in official Vulkan headers)
// Define maintenance 7-8 extension names
#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7"
#endif
#ifndef VK_KHR_MAINTENANCE_8_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_8_EXTENSION_NAME "VK_KHR_maintenance8"
#endif
#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9"
#endif
// Sanitize macros
#undef CreateEvent
@@ -43,19 +43,9 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
case 0xdff2e5c1u: // VUID-vkCmdSetRasterizerDiscardEnable-None-04871
case 0x0cc85f41u: // VUID-vkCmdSetPrimitiveRestartEnable-None-04866
case 0x01257b492: // VUID-vkCmdSetLogicOpEXT-None-0486
// The below are due to incorrect reporting of vertexInputDynamicState
case 0x398e0dabu: // VUID-vkCmdSetVertexInputEXT-None-04790
// The below are due to incorrect reporting of extendedDynamicState3
case 0x970c11a5u: // VUID-vkCmdSetColorWriteMaskEXT-extendedDynamicState3ColorWriteMask-07364
case 0x6b453f78u: // VUID-vkCmdSetColorBlendEnableEXT-extendedDynamicState3ColorBlendEnable-07355
case 0xf66469d0u: // VUID-vkCmdSetColorBlendEquationEXT-extendedDynamicState3ColorBlendEquation-07356
case 0x1d43405eu: // VUID-vkCmdSetLogicOpEnableEXT-extendedDynamicState3LogicOpEnable-07365
case 0x638462e8u: // VUID-vkCmdSetDepthClampEnableEXT-extendedDynamicState3DepthClampEnable-07448
// Misc
case 0xe0a2da61u: // VUID-vkCmdDrawIndexed-format-07753
#else
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
#else
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use
case 0x55362756u: // Descriptor binding and framebuffer attachment overlap
+61 -220
View File
@@ -475,6 +475,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (extensions.descriptor_indexing && Settings::values.descriptor_indexing.GetValue()) {
first_next = &descriptor_indexing;
} else {
RemoveExtension(extensions.descriptor_indexing,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
}
is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT);
@@ -498,13 +501,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
CollectToolingInfo();
if (is_qualcomm) {
// Qualcomm Adreno GPUs doesn't handle scaled vertex attributes; keep emulation enabled
must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan,
"Qualcomm drivers require scaled vertex format emulation; forcing fallback");
LOG_WARNING(Render_Vulkan,
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
@@ -523,9 +520,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
bool should_patch_bcn = api_level >= 28;
const bool bcn_debug_override = Settings::values.patch_old_qcom_drivers.GetValue();
if (bcn_debug_override != should_patch_bcn) {
LOG_WARNING(Render_Vulkan,
"BCn patch debug override active: {} (auto-detected: {})",
bcn_debug_override, should_patch_bcn);
should_patch_bcn = bcn_debug_override;
}
@@ -540,11 +534,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
} else {
LOG_ERROR(Render_Vulkan, "BCn patch failed! Driver code may now crash");
}
} else {
LOG_WARNING(Render_Vulkan,
"BCn texture patching skipped for stability (Android API {} < 28). "
"Driver version {}.{} would support patching, but may crash on older Android.",
api_level, major, minor);
}
} else if (patch_status == ADRENOTOOLS_BCN_BLOB) {
LOG_INFO(Render_Vulkan, "Adreno driver supports BCn textures natively (no patch needed)");
@@ -563,7 +552,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
features.shader_float16_int8.shaderFloat16 = false;
}
// Mali/ NVIDIA proprietary drivers: Shader stencil export not supported
// NVIDIA proprietary drivers: Shader stencil export not supported
if (properties.properties.driverVersion >= VK_MAKE_API_VERSION(510, 0, 0, 0)) {
LOG_WARNING(Render_Vulkan,
"NVIDIA Drivers >= 510 do not support MSAA->MSAA image blits. "
"MSAA scaling will use 3D helpers. MSAA resolves work normally.");
cant_blit_msaa = true;
}
// Use hardware depth/stencil blits instead when available
if (!extensions.shader_stencil_export) {
LOG_INFO(Render_Vulkan,
@@ -610,10 +606,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const size_t derived_budget =
(std::max)(MIN_SAMPLER_BUDGET, sampler_limit - reserved);
sampler_heap_budget = derived_budget;
LOG_WARNING(Render_Vulkan,
"Qualcomm driver reports max {} samplers; reserving {} (25%) and "
"allowing Eden to use {} (75%) to avoid heap exhaustion",
sampler_limit, reserved, sampler_heap_budget);
}
}
@@ -660,53 +652,25 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const auto dyna_state = Settings::values.dyna_state.GetValue();
// Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp
// This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs:
// Level 0 = Core Dynamic States only (Vulkan 1.0)
// Level 1 = Core + VK_EXT_extended_dynamic_state
// Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2
// Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3
switch (dyna_state) {
case Settings::ExtendedDynamicState::Disabled:
// Level 0: Disable all extended dynamic state extensions
// Level 0: Disable all configured extended dynamic state extensions
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
break;
case Settings::ExtendedDynamicState::EDS1:
// Level 1: Enable EDS1, disable EDS2 and EDS3
// Level 1: Enable EDS1, disable EDS2
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
break;
case Settings::ExtendedDynamicState::EDS2:
// Level 2: Enable EDS1 + EDS2, disable EDS3
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
break;
case Settings::ExtendedDynamicState::EDS3:
default:
// Level 3: Enable all (EDS1 + EDS2 + EDS3)
// Level 2: Enable EDS1 + EDS2
break;
}
// VK_EXT_vertex_input_dynamic_state is independent from EDS
// It can be enabled even without extended_dynamic_state
if (!Settings::values.vertex_input_dynamic_state.GetValue()) {
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);
graphics_queue = logical.GetQueue(graphics_family);
@@ -1063,6 +1027,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
SetNext(next, properties.push_descriptor);
}
if (extensions.conservative_rasterization) {
properties.conservative_rasterization.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT;
SetNext(next, properties.conservative_rasterization);
}
if (extensions.subgroup_size_control || features.subgroup_size_control.subgroupSizeControl) {
properties.subgroup_size_control.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
@@ -1093,6 +1062,22 @@ bool Device::GetSuitability(bool requires_swapchain) {
// Unload extensions if feature support is insufficient.
RemoveUnsuitableExtensions();
// Query VK_EXT_custom_border_color properties if the extension is enabled.
if (extensions.custom_border_color) {
auto proc = dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceCustomBorderColorPropertiesEXT");
if (proc != nullptr) {
auto vkGetPhysicalDeviceCustomBorderColorPropertiesEXT =
reinterpret_cast<void(*)(VkPhysicalDevice, VkPhysicalDeviceCustomBorderColorPropertiesEXT*)>(
proc);
custom_border_color_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
custom_border_color_properties.pNext = nullptr;
vkGetPhysicalDeviceCustomBorderColorPropertiesEXT(physical, &custom_border_color_properties);
has_custom_border_color_properties = true;
} else {
has_custom_border_color_properties = false;
}
}
// Check limits.
struct Limit {
u32 minimum;
@@ -1117,46 +1102,14 @@ bool Device::GetSuitability(bool requires_swapchain) {
// VK_DYNAMIC_STATE
// Driver detection variables for workarounds in GetSuitability
const VkDriverId driver_id = properties.driver.driverID;
// VK_EXT_extended_dynamic_state below this will appear drivers that need workarounds.
// VK_EXT_extended_dynamic_state2 below this will appear drivers that need workarounds.
// VK_EXT_extended_dynamic_state3 below this will appear drivers that need workarounds.
// Samsung: Broken extendedDynamicState3ColorBlendEquation
// Disable blend equation dynamic state, force static pipeline state
if (extensions.extended_dynamic_state3 &&
(driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
LOG_WARNING(Render_Vulkan,
"Samsung: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
}
// Intel Windows < 27.20.100.0: Broken VertexInputDynamicState
// Same for NVIDIA Proprietary < 580.119.02, unknown when VIDS was first NOT broken
// Disable VertexInputDynamicState on old Intel Windows drivers
if (extensions.vertex_input_dynamic_state) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if ((driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS && version < VK_MAKE_API_VERSION(27, 20, 100, 0))
|| (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && version < VK_MAKE_API_VERSION(580, 119, 02, 0))) {
LOG_WARNING(Render_Vulkan, "Disabling broken VK_EXT_vertex_input_dynamic_state");
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
if (u32(Settings::values.dyna_state.GetValue()) == 0) {
LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features");
features.custom_border_color.customBorderColors = false;
features.custom_border_color.customBorderColorWithoutFormat = false;
features.extended_dynamic_state.extendedDynamicState = false;
features.extended_dynamic_state2.extendedDynamicState2 = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = false;
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false;
}
// Return whether we were suitable.
@@ -1165,15 +1118,9 @@ bool Device::GetSuitability(bool requires_swapchain) {
void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color
// Enable extension if driver supports it, then check individual features
// - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
// - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED
// If only customBorderColors is available, we must provide a specific format
if (extensions.custom_border_color) {
// Verify that at least customBorderColors is available
if (!features.custom_border_color.customBorderColors) {
LOG_WARNING(Render_Vulkan,
"VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling");
extensions.custom_border_color = false;
}
}
@@ -1196,7 +1143,7 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
/* */ // VK_EXT_extended_dynamic_state
// VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
features.extended_dynamic_state,
@@ -1208,67 +1155,7 @@ void Device::RemoveUnsuitableExtensions() {
features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state3
const bool supports_color_blend_enable =
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable;
const bool supports_color_blend_equation =
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation;
const bool supports_color_write_mask =
features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask;
dynamic_state3_blending = supports_color_blend_enable && supports_color_blend_equation &&
supports_color_write_mask;
const bool supports_depth_clamp_enable =
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable;
const bool supports_logic_op_enable =
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable;
const bool supports_line_raster_mode =
features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode &&
extensions.line_rasterization && features.line_rasterization.rectangularLines;
const bool supports_conservative_raster_mode =
features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode &&
extensions.conservative_rasterization;
const bool supports_line_stipple_enable =
features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable &&
extensions.line_rasterization && features.line_rasterization.stippledRectangularLines;
const bool supports_alpha_to_coverage =
features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable;
const bool supports_alpha_to_one =
features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable &&
features.features.alphaToOne;
dynamic_state3_depth_clamp_enable = supports_depth_clamp_enable;
dynamic_state3_logic_op_enable = supports_logic_op_enable;
dynamic_state3_line_raster_mode = supports_line_raster_mode;
dynamic_state3_conservative_raster_mode = supports_conservative_raster_mode;
dynamic_state3_line_stipple_enable = supports_line_stipple_enable;
dynamic_state3_alpha_to_coverage = supports_alpha_to_coverage;
dynamic_state3_alpha_to_one = supports_alpha_to_one;
dynamic_state3_enables = dynamic_state3_depth_clamp_enable || dynamic_state3_logic_op_enable ||
dynamic_state3_line_raster_mode ||
dynamic_state3_conservative_raster_mode ||
dynamic_state3_line_stipple_enable ||
dynamic_state3_alpha_to_coverage || dynamic_state3_alpha_to_one;
extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
if (!extensions.extended_dynamic_state3) {
dynamic_state3_blending = false;
dynamic_state3_enables = false;
dynamic_state3_depth_clamp_enable = false;
dynamic_state3_logic_op_enable = false;
dynamic_state3_line_raster_mode = false;
dynamic_state3_conservative_raster_mode = false;
dynamic_state3_line_stipple_enable = false;
dynamic_state3_alpha_to_coverage = false;
dynamic_state3_alpha_to_one = false;
}
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_robustness2
// Enable if at least one robustness2 feature is available
extensions.robustness_2 = features.robustness2.robustBufferAccess2 ||
features.robustness2.robustImageAccess2 ||
features.robustness2.nullDescriptor;
@@ -1277,25 +1164,10 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// VK_EXT_image_robustness
// Enable if robustImageAccess is available
extensions.image_robustness = features.image_robustness.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
// VK_EXT_provoking_vertex
if (Settings::values.provoking_vertex.GetValue()) {
extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast
&& features.provoking_vertex
.transformFeedbackPreservesProvokingVertex;
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex,
features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
} else {
RemoveExtensionFeature(extensions.provoking_vertex,
features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
}
// VK_KHR_shader_atomic_int64
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
features.shader_atomic_int64.shaderSharedInt64Atomics;
@@ -1319,28 +1191,32 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
// VK_EXT_transform_feedback
// We only require the basic transformFeedback feature and at least
// one transform feedback buffer. We keep transformFeedbackQueries as it's used by
// the streaming byte count implementation. GeometryStreams and multiple streams
// are not strictly required since we currently support only stream 0.
extensions.transform_feedback =
features.transform_feedback.transformFeedback &&
properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
properties.transform_feedback.transformFeedbackQueries;
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
if (extensions.transform_feedback) {
LOG_INFO(Render_Vulkan, "VK_EXT_transform_feedback enabled (buffers={}, queries={})",
properties.transform_feedback.maxTransformFeedbackBuffers,
properties.transform_feedback.transformFeedbackQueries);
}
// VK_EXT_vertex_input_dynamic_state
extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
// VK_EXT_provoking_vertex
extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast;
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex,
features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
// VK_EXT_line_rasterization
extensions.line_rasterization = features.line_rasterization.rectangularLines ||
features.line_rasterization.bresenhamLines ||
features.line_rasterization.smoothLines;
RemoveExtensionFeatureIfUnsuitable(extensions.line_rasterization,
features.line_rasterization,
VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
// VK_EXT_conditional_rendering
extensions.conditional_rendering = features.conditional_rendering.conditionalRendering;
RemoveExtensionFeatureIfUnsuitable(extensions.conditional_rendering,
features.conditional_rendering,
VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME);
// VK_EXT_multi_draw
extensions.multi_draw = features.multi_draw.multiDraw;
@@ -1377,35 +1253,15 @@ void Device::RemoveUnsuitableExtensions() {
features.workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
// VK_EXT_swapchain_maintenance1 (extension only, has features)
// Requires VK_EXT_surface_maintenance1 instance extension
extensions.swapchain_maintenance1 = features.swapchain_maintenance1.swapchainMaintenance1;
if (extensions.swapchain_maintenance1) {
// Check if VK_EXT_surface_maintenance1 instance extension is available
const auto instance_extensions = vk::EnumerateInstanceExtensionProperties(dld);
const bool has_surface_maintenance1 = instance_extensions && std::ranges::any_of(*instance_extensions,
[](const VkExtensionProperties& prop) {
return std::strcmp(prop.extensionName, VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) == 0;
});
if (!has_surface_maintenance1) {
LOG_WARNING(Render_Vulkan,
"VK_EXT_swapchain_maintenance1 requires VK_EXT_surface_maintenance1, disabling");
extensions.swapchain_maintenance1 = false;
features.swapchain_maintenance1.swapchainMaintenance1 = false;
}
}
RemoveExtensionFeatureIfUnsuitable(extensions.swapchain_maintenance1, features.swapchain_maintenance1,
VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
// VK_KHR_maintenance1 (core in Vulkan 1.1, no features)
// VK_KHR_maintenance1
extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
// VK_KHR_maintenance2 (core in Vulkan 1.1, no features)
// VK_KHR_maintenance2
extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
// VK_KHR_maintenance3 (core in Vulkan 1.1, no features)
// VK_KHR_maintenance3
extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
@@ -1416,17 +1272,6 @@ void Device::RemoveUnsuitableExtensions() {
// VK_KHR_maintenance5
extensions.maintenance5 = features.maintenance5.maintenance5;
if (extensions.maintenance5) {
LOG_INFO(Render_Vulkan, "VK_KHR_maintenance5 properties: polygonModePointSize={} "
"depthStencilSwizzleOne={} earlyFragmentTests={} nonStrictWideLines={}",
properties.maintenance5.polygonModePointSize,
properties.maintenance5.depthStencilSwizzleOneSupport,
properties.maintenance5.earlyFragmentMultisampleCoverageAfterSampleCounting &&
properties.maintenance5.earlyFragmentSampleMaskTestBeforeSampleCounting,
properties.maintenance5.nonStrictWideLinesUseParallelogram);
}
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance5, features.maintenance5,
VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
@@ -1435,17 +1280,13 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6,
VK_KHR_MAINTENANCE_6_EXTENSION_NAME);
// VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features)
// VK_KHR_maintenance7
extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
// VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features)
// VK_KHR_maintenance8
extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
// VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features)
extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
}
void Device::SetupFamilies(VkSurfaceKHR surface) {
+61 -89
View File
@@ -48,12 +48,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
// Define all features which may be used by the implementation and require an extension here.
#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \
FEATURE(EXT, ConditionalRendering, CONDITIONAL_RENDERING, conditional_rendering) \
FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \
FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \
FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \
FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
@@ -63,8 +63,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
FEATURE(EXT, SwapchainMaintenance1, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
@@ -76,7 +74,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
// Define miscellaneous extensions which may be used by the implementation here.
#define FOR_EACH_VK_EXTENSION(EXTENSION) \
EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \
EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
@@ -100,7 +97,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(KHR, MAINTENANCE_3, maintenance3) \
EXTENSION(KHR, MAINTENANCE_7, maintenance7) \
EXTENSION(KHR, MAINTENANCE_8, maintenance8) \
EXTENSION(KHR, MAINTENANCE_9, maintenance9) \
EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \
EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \
EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
@@ -125,13 +121,11 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
EXTENSION_NAME(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME) \
EXTENSION_NAME(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME) \
EXTENSION_NAME(VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME)
@@ -189,8 +183,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE_NAME(shader_float16_int8, shaderInt8) \
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
FEATURE_NAME(transform_feedback, transformFeedback) \
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout)
// These features are not required but can be helpful for drivers that can use it.
#define FOR_EACH_VK_OPTIONAL_FEATURE(FEATURE_NAME) \
@@ -448,7 +441,7 @@ public:
return extensions.viewport_array2;
}
/// Returns true if the device supporst VK_EXT_DESCRIPTOR_INDEXING
/// Returns true if the device supporst VK_EXT_descriptor_indexing.
bool isExtDescriptorIndexingSupported() const {
return extensions.descriptor_indexing;
}
@@ -478,11 +471,6 @@ public:
return extensions.swapchain_mutable_format;
}
/// Returns true if VK_EXT_swapchain_maintenance1 is enabled.
bool IsExtSwapchainMaintenance1Enabled() const {
return extensions.swapchain_maintenance1;
}
/// Returns true if VK_KHR_shader_float_controls is enabled.
bool IsKhrShaderFloatControlsSupported() const {
return extensions.shader_float_controls;
@@ -519,13 +507,11 @@ public:
}
/// Returns true if the device supports VK_EXT_shader_stencil_export.
/// Note: Most Mali/NVIDIA drivers don't support this. Use hardware blits as fallback.
bool IsExtShaderStencilExportSupported() const {
return extensions.shader_stencil_export;
}
/// Returns true if depth/stencil operations can be performed efficiently.
/// Either through shader export or hardware blits.
/// Returns true if depth/stencil operations through shader export or hardware blits.
bool CanPerformDepthStencilOperations() const {
return extensions.shader_stencil_export || is_blit_depth24_stencil8_supported ||
is_blit_depth32_stencil8_supported;
@@ -561,11 +547,14 @@ public:
return extensions.transform_feedback;
}
/// Returns true if the device supports VK_EXT_transform_feedback properly.
bool AreTransformFeedbackGeometryStreamsSupported() const {
return features.transform_feedback.geometryStreams;
}
bool IsTransformFeedbackProvokingVertexPreserved() const {
return features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
}
/// Returns true if the device supports VK_EXT_custom_border_color.
bool IsExtCustomBorderColorSupported() const {
return extensions.custom_border_color;
@@ -611,6 +600,16 @@ public:
return features.custom_border_color.customBorderColorWithoutFormat;
}
/// Returns true if physical device custom border color properties were queried.
bool HasCustomBorderColorProperties() const {
return has_custom_border_color_properties;
}
/// Returns the queried VkPhysicalDeviceCustomBorderColorPropertiesEXT.
const VkPhysicalDeviceCustomBorderColorPropertiesEXT& GetCustomBorderColorProperties() const {
return custom_border_color_properties;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
bool IsExtExtendedDynamicStateSupported() const {
return extensions.extended_dynamic_state;
@@ -625,32 +624,21 @@ public:
return features.extended_dynamic_state2.extendedDynamicState2LogicOp;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3Supported() const {
return extensions.extended_dynamic_state3;
}
/// Returns true if the device supports VK_EXT_4444_formats.
bool IsExt4444FormatsSupported() const {
return features.format_a4b4g4r4.formatA4B4G4R4;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3BlendingSupported() const {
return dynamic_state3_blending;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3EnablesSupported() const {
return dynamic_state3_enables;
}
/// Returns true if the device supports VK_EXT_filter_cubic
/// Returns true if the device supports VK_EXT_filter_cubic.
bool IsExtFilterCubicSupported() const {
return extensions.filter_cubic;
}
/// Returns true if the device supports VK_QCOM_filter_cubic_weights
/// Custom border color properties retrieved from the physical device (if available).
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color_properties{};
bool has_custom_border_color_properties = false;
/// Returns true if the device supports VK_QCOM_filter_cubic_weights.
bool IsQcomFilterCubicWeightsSupported() const {
return extensions.filter_cubic_weights;
}
@@ -661,55 +649,36 @@ public:
}
bool SupportsRectangularLines() const {
return features.line_rasterization.rectangularLines != VK_FALSE;
return features.line_rasterization.rectangularLines;
}
bool SupportsBresenhamLines() const {
return features.line_rasterization.bresenhamLines;
}
bool SupportsSmoothLines() const {
return features.line_rasterization.smoothLines != VK_FALSE;
return features.line_rasterization.smoothLines;
}
bool SupportsStippledRectangularLines() const {
return features.line_rasterization.stippledRectangularLines != VK_FALSE;
return features.line_rasterization.stippledRectangularLines;
}
bool SupportsStippledBresenhamLines() const {
return features.line_rasterization.stippledBresenhamLines;
}
bool SupportsStippledSmoothLines() const {
return features.line_rasterization.stippledSmoothLines;
}
/// Returns true if the device supports AlphaToOne.
bool SupportsAlphaToOne() const {
return features.features.alphaToOne != VK_FALSE;
return features.features.alphaToOne;
}
bool SupportsDynamicState3DepthClampEnable() const {
return dynamic_state3_depth_clamp_enable;
}
bool SupportsDynamicState3LogicOpEnable() const {
return dynamic_state3_logic_op_enable;
}
bool SupportsDynamicState3LineRasterizationMode() const {
return dynamic_state3_line_raster_mode;
}
bool SupportsDynamicState3ConservativeRasterizationMode() const {
return dynamic_state3_conservative_raster_mode;
}
bool SupportsDynamicState3LineStippleEnable() const {
return dynamic_state3_line_stipple_enable;
}
bool SupportsDynamicState3AlphaToCoverageEnable() const {
return dynamic_state3_alpha_to_coverage;
}
bool SupportsDynamicState3AlphaToOneEnable() const {
return dynamic_state3_alpha_to_one;
}
/// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
bool IsExtVertexInputDynamicStateSupported() const {
return extensions.vertex_input_dynamic_state;
}
/// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation
/// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation.
bool IsExtShaderDemoteToHelperInvocationSupported() const {
return extensions.shader_demote_to_helper_invocation;
}
@@ -718,21 +687,37 @@ public:
bool IsExtConservativeRasterizationSupported() const {
return extensions.conservative_rasterization;
}
/// Returns true if the device supports conservative rasterization for points and lines.
bool SupportsConservativePointAndLineRasterization() const {
return extensions.conservative_rasterization &&
properties.conservative_rasterization.conservativePointAndLineRasterization;
}
/// Returns true if the device supports VK_EXT_provoking_vertex.
bool IsExtProvokingVertexSupported() const {
return extensions.provoking_vertex;
}
/// Returns true if the device supports provoking-vertex LAST mode.
bool IsProvokingVertexLastSupported() const {
return features.provoking_vertex.provokingVertexLast;
}
/// Returns true if the device supports VK_KHR_shader_atomic_int64.
bool IsExtShaderAtomicInt64Supported() const {
return extensions.shader_atomic_int64;
}
bool IsExtConditionalRendering() const {
/// Returns true if the device supports VK_EXT_conditional_rendering.
bool IsExtConditionalRenderingSupported() const {
return extensions.conditional_rendering;
}
bool IsExtConditionalRendering() const {
return IsExtConditionalRenderingSupported();
}
bool HasTimelineSemaphore() const;
/// Returns the minimum supported version of SPIR-V.
@@ -751,7 +736,7 @@ public:
return has_renderdoc || has_nsight_graphics || has_radeon_gpu_profiler;
}
/// @returns True if compute pipelines can cause crashing.
/// Returns true if compute pipelines can cause crashing.
bool HasBrokenCompute() const {
return has_broken_compute;
}
@@ -898,11 +883,6 @@ public:
return extensions.maintenance8;
}
/// Returns true if the device supports VK_KHR_maintenance9.
bool IsKhrMaintenance9Supported() const {
return extensions.maintenance9;
}
/// Returns true if the device supports UINT8 index buffer conversion via compute shader.
bool SupportsUint8Indices() const {
return features.bit8_storage.storageBuffer8BitAccess &&
@@ -1027,6 +1007,7 @@ private:
VkPhysicalDeviceSubgroupProperties subgroup_properties{};
VkPhysicalDeviceFloatControlsProperties float_controls{};
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{};
VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_rasterization{};
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
@@ -1059,15 +1040,6 @@ private:
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3.
bool dynamic_state3_depth_clamp_enable{};
bool dynamic_state3_logic_op_enable{};
bool dynamic_state3_line_raster_mode{};
bool dynamic_state3_conservative_raster_mode{};
bool dynamic_state3_line_stipple_enable{};
bool dynamic_state3_alpha_to_coverage{};
bool dynamic_state3_alpha_to_one{};
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited).
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
@@ -81,14 +81,6 @@ namespace {
#endif
if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}))
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
// VK_EXT_surface_maintenance1 is required for VK_EXT_swapchain_maintenance1
if (window_type != Core::Frontend::WindowSystemType::Headless && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME})) {
extensions.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
// Some(which?) drivers dont like being told to load this extension(why?)
// NVIDIA on FreeBSD is totally fine with this through
if (AreExtensionsSupported(dld, *properties, std::array{VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME}))
extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
}
}
return extensions;
}
+13 -12
View File
@@ -127,18 +127,20 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdPipelineBarrier);
X(vkCmdPushConstants);
X(vkCmdPushDescriptorSetWithTemplateKHR);
X(vkCmdResetQueryPool);
X(vkCmdSetBlendConstants);
X(vkCmdSetDepthBias);
X(vkCmdSetDepthBias2EXT);
X(vkCmdSetDepthBounds);
X(vkCmdSetEvent);
X(vkCmdSetScissor);
X(vkCmdSetScissorWithCountEXT);
X(vkCmdSetStencilCompareMask);
X(vkCmdSetStencilReference);
X(vkCmdSetStencilWriteMask);
X(vkCmdSetViewport);
X(vkCmdSetViewportWithCountEXT);
X(vkCmdWaitEvents);
X(vkCmdBindVertexBuffers2EXT);
X(vkCmdSetCullModeEXT);
X(vkCmdSetDepthBoundsTestEnableEXT);
X(vkCmdSetDepthCompareOpEXT);
@@ -146,25 +148,15 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdSetDepthWriteEnableEXT);
X(vkCmdSetPrimitiveRestartEnableEXT);
X(vkCmdSetRasterizerDiscardEnableEXT);
X(vkCmdSetAlphaToCoverageEnableEXT);
X(vkCmdSetAlphaToOneEnableEXT);
X(vkCmdSetConservativeRasterizationModeEXT);
X(vkCmdSetLineRasterizationModeEXT);
X(vkCmdSetLineStippleEnableEXT);
X(vkCmdSetDepthBiasEnableEXT);
X(vkCmdSetLogicOpEnableEXT);
X(vkCmdSetDepthClampEnableEXT);
X(vkCmdSetFrontFaceEXT);
X(vkCmdSetLogicOpEXT);
X(vkCmdSetPatchControlPointsEXT);
X(vkCmdSetLineStippleEXT);
X(vkCmdSetLineWidth);
X(vkCmdSetPrimitiveTopologyEXT);
X(vkCmdSetStencilOpEXT);
X(vkCmdSetStencilTestEnableEXT);
X(vkCmdSetVertexInputEXT);
X(vkCmdSetColorWriteMaskEXT);
X(vkCmdSetColorBlendEnableEXT);
X(vkCmdSetColorBlendEquationEXT);
X(vkCmdResolveImage);
X(vkCreateBuffer);
X(vkCreateBufferView);
@@ -254,6 +246,15 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device);
Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device);
}
if (!dld.vkCmdSetPrimitiveTopologyEXT) {
Proc(dld.vkCmdSetPrimitiveTopologyEXT, dld, "vkCmdSetPrimitiveTopology", device);
}
if (!dld.vkCmdSetViewportWithCountEXT) {
Proc(dld.vkCmdSetViewportWithCountEXT, dld, "vkCmdSetViewportWithCount", device);
}
if (!dld.vkCmdSetScissorWithCountEXT) {
Proc(dld.vkCmdSetScissorWithCountEXT, dld, "vkCmdSetScissorWithCount", device);
}
#undef X
}
+91 -97
View File
@@ -200,7 +200,6 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdBindPipeline vkCmdBindPipeline{};
PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{};
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{};
PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{};
PFN_vkCmdBlitImage vkCmdBlitImage{};
PFN_vkCmdClearAttachments vkCmdClearAttachments{};
PFN_vkCmdClearColorImage vkCmdClearColorImage{};
@@ -229,6 +228,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
PFN_vkCmdPushConstants vkCmdPushConstants{};
PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{};
PFN_vkCmdResetQueryPool vkCmdResetQueryPool{};
PFN_vkCmdResolveImage vkCmdResolveImage{};
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{};
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{};
@@ -241,15 +241,8 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
PFN_vkCmdSetAlphaToCoverageEnableEXT vkCmdSetAlphaToCoverageEnableEXT{};
PFN_vkCmdSetAlphaToOneEnableEXT vkCmdSetAlphaToOneEnableEXT{};
PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT{};
PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{};
PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{};
PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT{};
PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{};
PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{};
PFN_vkCmdSetEvent vkCmdSetEvent{};
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{};
@@ -257,16 +250,14 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
PFN_vkCmdSetScissor vkCmdSetScissor{};
PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT{};
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{};
PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{};
PFN_vkCmdSetStencilReference vkCmdSetStencilReference{};
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{};
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
PFN_vkCmdSetViewport vkCmdSetViewport{};
PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{};
PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{};
PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{};
PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT{};
PFN_vkCmdWaitEvents vkCmdWaitEvents{};
PFN_vkCreateBuffer vkCreateBuffer{};
PFN_vkCreateBufferView vkCreateBufferView{};
@@ -1182,6 +1173,10 @@ public:
dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data);
}
void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept {
dld->vkCmdResetQueryPool(handle, query_pool, first, count);
}
void BindPipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline) const noexcept {
dld->vkCmdBindPipeline(handle, bind_point, pipeline);
}
@@ -1374,6 +1369,18 @@ public:
dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data());
}
void SetViewportWithCountEXT(Span<VkViewport> viewports) const noexcept {
if (dld && dld->vkCmdSetViewportWithCountEXT) {
dld->vkCmdSetViewportWithCountEXT(handle, viewports.size(), viewports.data());
}
}
void SetScissorWithCountEXT(Span<VkRect2D> scissors) const noexcept {
if (dld && dld->vkCmdSetScissorWithCountEXT) {
dld->vkCmdSetScissorWithCountEXT(handle, scissors.size(), scissors.data());
}
}
void SetBlendConstants(const float blend_constants[4]) const noexcept {
dld->vkCmdSetBlendConstants(handle, blend_constants);
}
@@ -1403,7 +1410,9 @@ public:
.depthBiasClamp = clamp,
.depthBiasSlopeFactor = slope_factor,
};
dld->vkCmdSetDepthBias2EXT(handle, &info);
if (dld && dld->vkCmdSetDepthBias2EXT) {
dld->vkCmdSetDepthBias2EXT(handle, &info);
}
}
void SetDepthBounds(float min_depth_bounds, float max_depth_bounds) const noexcept {
@@ -1423,104 +1432,76 @@ public:
buffer_barriers.data(), image_barriers.size(), image_barriers.data());
}
void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
const VkDeviceSize* strides) const noexcept {
dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
sizes, strides);
}
void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
dld->vkCmdSetCullModeEXT(handle, cull_mode);
if (dld && dld->vkCmdSetCullModeEXT) {
dld->vkCmdSetCullModeEXT(handle, cull_mode);
}
}
void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetDepthBoundsTestEnableEXT) {
dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
if (dld && dld->vkCmdSetDepthCompareOpEXT) {
dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
}
}
void SetDepthTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetDepthTestEnableEXT) {
dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetDepthWriteEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetDepthWriteEnableEXT) {
dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetPrimitiveRestartEnableEXT(bool enable) const noexcept {
dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetPrimitiveRestartEnableEXT) {
dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetRasterizerDiscardEnableEXT(bool enable) const noexcept {
dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetRasterizerDiscardEnableEXT) {
dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetConservativeRasterizationModeEXT(VkConservativeRasterizationModeEXT mode) const noexcept
{
dld->vkCmdSetConservativeRasterizationModeEXT(handle, mode);
}
void SetLineRasterizationModeEXT(VkLineRasterizationModeEXT mode) const noexcept
{
dld->vkCmdSetLineRasterizationModeEXT(handle, mode);
}
void SetLineStippleEnableEXT(bool enable) const noexcept
{
dld->vkCmdSetLineStippleEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept
{
dld->vkCmdSetLineStippleEXT(handle, factor, pattern);
void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept {
if (dld && dld->vkCmdSetLineStippleEXT) {
dld->vkCmdSetLineStippleEXT(handle, factor, pattern);
}
}
void SetDepthBiasEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetLogicOpEnableEXT(bool enable) const noexcept {
dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetAlphaToCoverageEnableEXT(bool enable) const noexcept {
dld->vkCmdSetAlphaToCoverageEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetAlphaToOneEnableEXT(bool enable) const noexcept {
dld->vkCmdSetAlphaToOneEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthClampEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
if (dld && dld->vkCmdSetDepthBiasEnableEXT) {
dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
dld->vkCmdSetFrontFaceEXT(handle, front_face);
if (dld && dld->vkCmdSetFrontFaceEXT) {
dld->vkCmdSetFrontFaceEXT(handle, front_face);
}
}
void SetLogicOpEXT(VkLogicOp logic_op) const noexcept {
dld->vkCmdSetLogicOpEXT(handle, logic_op);
if (dld && dld->vkCmdSetLogicOpEXT) {
dld->vkCmdSetLogicOpEXT(handle, logic_op);
}
}
void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept {
dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
}
void SetColorWriteMaskEXT(u32 first, Span<VkColorComponentFlags> masks) const noexcept {
dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data());
}
void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept {
dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data());
}
void SetColorBlendEquationEXT(u32 first,
Span<VkColorBlendEquationEXT> equations) const noexcept {
dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data());
if (dld && dld->vkCmdSetPatchControlPointsEXT) {
dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
}
}
void SetLineWidth(float line_width) const noexcept {
@@ -1528,52 +1509,61 @@ public:
}
void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
if (dld && dld->vkCmdSetPrimitiveTopologyEXT) {
dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
}
}
void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
if (dld && dld->vkCmdSetStencilOpEXT) {
dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
}
}
void SetStencilTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetVertexInputEXT(
vk::Span<VkVertexInputBindingDescription2EXT> bindings,
vk::Span<VkVertexInputAttributeDescription2EXT> attributes) const noexcept {
dld->vkCmdSetVertexInputEXT(handle, bindings.size(), bindings.data(), attributes.size(),
attributes.data());
if (dld && dld->vkCmdSetStencilTestEnableEXT) {
dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
}
void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes) const noexcept {
dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes);
if (dld && dld->vkCmdBindTransformFeedbackBuffersEXT) {
dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes);
}
}
void BeginTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count,
const VkBuffer* counter_buffers,
const VkDeviceSize* counter_buffer_offsets) const noexcept {
dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
if (dld && dld->vkCmdBeginTransformFeedbackEXT) {
dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
}
}
void EndTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count,
const VkBuffer* counter_buffers,
const VkDeviceSize* counter_buffer_offsets) const noexcept {
dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
if (dld && dld->vkCmdEndTransformFeedbackEXT) {
dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
}
}
void BeginConditionalRenderingEXT(
const VkConditionalRenderingBeginInfoEXT& info) const noexcept {
dld->vkCmdBeginConditionalRenderingEXT(handle, &info);
if (dld && dld->vkCmdBeginConditionalRenderingEXT) {
dld->vkCmdBeginConditionalRenderingEXT(handle, &info);
}
}
void EndConditionalRenderingEXT() const noexcept {
dld->vkCmdEndConditionalRenderingEXT(handle);
if (dld && dld->vkCmdEndConditionalRenderingEXT) {
dld->vkCmdEndConditionalRenderingEXT(handle);
}
}
void BeginDebugUtilsLabelEXT(const char* label, std::span<float, 4> color) const noexcept {
@@ -1583,11 +1573,15 @@ public:
.pLabelName = label,
.color{color[0], color[1], color[2], color[3]},
};
dld->vkCmdBeginDebugUtilsLabelEXT(handle, &label_info);
if (dld && dld->vkCmdBeginDebugUtilsLabelEXT) {
dld->vkCmdBeginDebugUtilsLabelEXT(handle, &label_info);
}
}
void EndDebugUtilsLabelEXT() const noexcept {
dld->vkCmdEndDebugUtilsLabelEXT(handle);
if (dld && dld->vkCmdEndDebugUtilsLabelEXT) {
dld->vkCmdEndDebugUtilsLabelEXT(handle);
}
}
private: