Skip to content

Commit 6148459

Browse files
committed
Vulkan: Use per-pipeline buffer robustness
And if the extension is not supported then fallback to enabling robust buffer access for all shaders.
1 parent 081ebea commit 6148459

File tree

6 files changed

+95
-11
lines changed

6 files changed

+95
-11
lines changed

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ void PipelineCompiler::InitDynamicState(PipelineInfo* pipelineInfo, bool usesBle
873873
dynamicState.pDynamicStates = dynamicStates.data();
874874
}
875875

876-
bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const LatteContextRegister& latteRegister, VKRObjectRenderPass* renderPassObj)
876+
bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const LatteContextRegister& latteRegister, VKRObjectRenderPass* renderPassObj, bool requireRobustBufferAccess)
877877
{
878878
VulkanRenderer* vkRenderer = VulkanRenderer::GetInstance();
879879

@@ -888,6 +888,7 @@ bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const
888888
m_vkGeometryShader = pipelineInfo->geometryShaderVk;
889889
m_vkrObjPipeline = pipelineInfo->m_vkrObjPipeline;
890890
m_renderPassObj = renderPassObj;
891+
m_requestRobustBufferAccess = requireRobustBufferAccess;
891892

892893
// if required generate RECT emulation geometry shader
893894
if (!vkRenderer->m_featureControl.deviceExtensions.nv_fill_rectangle && isPrimitiveRect)
@@ -998,6 +999,8 @@ bool PipelineCompiler::Compile(bool forceCompile, bool isRenderThread, bool show
998999
if (!forceCompile)
9991000
pipelineInfo.flags |= VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT;
10001001

1002+
void* prevStruct = nullptr;
1003+
10011004
VkPipelineCreationFeedbackCreateInfoEXT creationFeedbackInfo;
10021005
VkPipelineCreationFeedbackEXT creationFeedback;
10031006
std::vector<VkPipelineCreationFeedbackEXT> creationStageFeedback(0);
@@ -1015,9 +1018,25 @@ bool PipelineCompiler::Compile(bool forceCompile, bool isRenderThread, bool show
10151018
creationFeedbackInfo.pPipelineCreationFeedback = &creationFeedback;
10161019
creationFeedbackInfo.pPipelineStageCreationFeedbacks = creationStageFeedback.data();
10171020
creationFeedbackInfo.pipelineStageCreationFeedbackCount = pipelineInfo.stageCount;
1018-
pipelineInfo.pNext = &creationFeedbackInfo;
1021+
creationFeedbackInfo.pNext = prevStruct;
1022+
prevStruct = &creationFeedbackInfo;
10191023
}
10201024

1025+
VkPipelineRobustnessCreateInfoEXT pipelineRobustnessCreateInfo{};
1026+
if (vkRenderer->m_featureControl.deviceExtensions.pipeline_robustness && m_requestRobustBufferAccess)
1027+
{
1028+
// per-pipeline handling of robust buffer access, if the extension is not available then we fall back to device feature robustBufferAccess
1029+
pipelineRobustnessCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT;
1030+
pipelineRobustnessCreateInfo.pNext = prevStruct;
1031+
prevStruct = &pipelineRobustnessCreateInfo;
1032+
pipelineRobustnessCreateInfo.storageBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT;
1033+
pipelineRobustnessCreateInfo.uniformBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT;
1034+
pipelineRobustnessCreateInfo.vertexInputs = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT;
1035+
pipelineRobustnessCreateInfo.images = VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT_EXT;
1036+
}
1037+
1038+
pipelineInfo.pNext = prevStruct;
1039+
10211040
VkPipeline pipeline = VK_NULL_HANDLE;
10221041
VkResult result;
10231042
uint8 retryCount = 0;
@@ -1075,3 +1094,31 @@ void PipelineCompiler::TrackAsCached(uint64 baseHash, uint64 pipelineStateHash)
10751094
return;
10761095
pipelineCache.AddCurrentStateToCache(baseHash, pipelineStateHash);
10771096
}
1097+
1098+
// calculate whether the pipeline requires robust buffer access
1099+
// if there is a potential risk for a shader to do out-of-bounds reads or writes we need to enable robust buffer access
1100+
// this can happen when:
1101+
// - Streamout is used with too small of a buffer (probably? Could also be some issue with how the streamout array index is calculated -> We can maybe fix this in the future)
1102+
// - The shader uses dynamic indices for uniform access. This will trigger the uniform mode to be FULL_CBANK
1103+
bool PipelineCompiler::CalcRobustBufferAccessRequirement(LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader, LatteDecompilerShader* geometryShader)
1104+
{
1105+
bool requiresRobustBufferAcces = false;
1106+
if (vertexShader)
1107+
{
1108+
cemu_assert_debug(vertexShader->shaderType == LatteConst::ShaderType::Vertex);
1109+
requiresRobustBufferAcces |= vertexShader->hasStreamoutBufferWrite;
1110+
requiresRobustBufferAcces |= vertexShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK;
1111+
}
1112+
if (geometryShader)
1113+
{
1114+
cemu_assert_debug(geometryShader->shaderType == LatteConst::ShaderType::Geometry);
1115+
requiresRobustBufferAcces |= geometryShader->hasStreamoutBufferWrite;
1116+
requiresRobustBufferAcces |= geometryShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK;
1117+
}
1118+
if (pixelShader)
1119+
{
1120+
cemu_assert_debug(pixelShader->shaderType == LatteConst::ShaderType::Pixel);
1121+
requiresRobustBufferAcces |= pixelShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK;
1122+
}
1123+
return requiresRobustBufferAcces;
1124+
}

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ class PipelineCompiler : public VKRMoveableRefCounter
3838
RendererShaderVk* m_vkPixelShader{};
3939
RendererShaderVk* m_vkGeometryShader{};
4040

41-
bool InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const LatteContextRegister& latteRegister, VKRObjectRenderPass* renderPassObj);
41+
bool InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const LatteContextRegister& latteRegister, VKRObjectRenderPass* renderPassObj, bool requireRobustBufferAccess);
4242
void TrackAsCached(uint64 baseHash, uint64 pipelineStateHash); // stores pipeline to permanent cache if not yet cached. Must be called synchronously from render thread due to dependency on GPU state
4343

44+
static bool CalcRobustBufferAccessRequirement(LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader, LatteDecompilerShader* geometryShader);
45+
4446
VkPipelineLayout m_pipelineLayout;
4547
VKRObjectRenderPass* m_renderPassObj{};
48+
bool m_requestRobustBufferAccess{false};
4649

4750
/* shader stages */
4851
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,17 @@ void VulkanPipelineStableCache::LoadPipelineFromCache(std::span<uint8> fileData)
277277
m_pipelineIsCachedLock.unlock();
278278
// compile
279279
{
280-
PipelineCompiler pp;
281-
if (!pp.InitFromCurrentGPUState(pipelineInfo, *lcr, renderPass))
280+
PipelineCompiler pipelineCompiler;
281+
bool requiresRobustBufferAccess = PipelineCompiler::CalcRobustBufferAccessRequirement(vertexShader, pixelShader, geometryShader);
282+
if (!pipelineCompiler.InitFromCurrentGPUState(pipelineInfo, *lcr, renderPass, requiresRobustBufferAccess))
282283
{
283284
s_spinlockSharedInternal.lock();
284285
delete lcr;
285286
delete cachedPipeline;
286287
s_spinlockSharedInternal.unlock();
287288
return;
288289
}
289-
pp.Compile(true, true, false);
290-
// destroy pp early
290+
pipelineCompiler.Compile(true, true, false);
291291
}
292292
// on success, calculate pipeline hash and flag as present in cache
293293
uint64 pipelineBaseHash = vertexShader->baseHash;

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ const std::vector<const char*> kOptionalDeviceExtensions =
5050
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
5151
VK_KHR_PRESENT_WAIT_EXTENSION_NAME,
5252
VK_KHR_PRESENT_ID_EXTENSION_NAME,
53-
VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME
53+
VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
54+
VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME
5455
};
5556

5657
const std::vector<const char*> kRequiredDeviceExtensions =
@@ -263,6 +264,14 @@ void VulkanRenderer::GetDeviceFeatures()
263264
pwf.pNext = prevStruct;
264265
prevStruct = &pwf;
265266

267+
VkPhysicalDevicePipelineRobustnessFeaturesEXT pprf{};
268+
if (m_featureControl.deviceExtensions.pipeline_robustness)
269+
{
270+
pprf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT;
271+
pprf.pNext = prevStruct;
272+
prevStruct = &pprf;
273+
}
274+
266275
VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{};
267276
physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
268277
physicalDeviceFeatures2.pNext = prevStruct;
@@ -317,6 +326,11 @@ void VulkanRenderer::GetDeviceFeatures()
317326
{
318327
cemuLog_log(LogType::Force, "VK_EXT_depth_clip_enable not supported");
319328
}
329+
if (m_featureControl.deviceExtensions.pipeline_robustness)
330+
{
331+
if ( pprf.pipelineRobustness != VK_TRUE )
332+
m_featureControl.deviceExtensions.pipeline_robustness = false;
333+
}
320334
// get limits
321335
m_featureControl.limits.minUniformBufferOffsetAlignment = std::max(prop2.properties.limits.minUniformBufferOffsetAlignment, (VkDeviceSize)4);
322336
m_featureControl.limits.nonCoherentAtomSize = std::max(prop2.properties.limits.nonCoherentAtomSize, (VkDeviceSize)4);
@@ -475,11 +489,17 @@ VulkanRenderer::VulkanRenderer()
475489
deviceFeatures.occlusionQueryPrecise = VK_TRUE;
476490
deviceFeatures.depthClamp = VK_TRUE;
477491
deviceFeatures.depthBiasClamp = VK_TRUE;
478-
if (m_vendor == GfxVendor::AMD)
492+
493+
if (m_featureControl.deviceExtensions.pipeline_robustness)
479494
{
495+
deviceFeatures.robustBufferAccess = VK_FALSE;
496+
}
497+
else
498+
{
499+
cemuLog_log(LogType::Force, "VK_EXT_pipeline_robustness not supported. Falling back to robustBufferAccess");
480500
deviceFeatures.robustBufferAccess = VK_TRUE;
481-
cemuLog_log(LogType::Force, "Enable robust buffer access");
482501
}
502+
483503
if (m_featureControl.mode.useTFEmulationViaSSBO)
484504
{
485505
deviceFeatures.vertexPipelineStoresAndAtomics = true;
@@ -524,6 +544,15 @@ VulkanRenderer::VulkanRenderer()
524544
deviceExtensionFeatures = &presentWaitFeature;
525545
presentWaitFeature.presentWait = VK_TRUE;
526546
}
547+
// enable VK_EXT_pipeline_robustness
548+
VkPhysicalDevicePipelineRobustnessFeaturesEXT pipelineRobustnessFeature{};
549+
if (m_featureControl.deviceExtensions.pipeline_robustness)
550+
{
551+
pipelineRobustnessFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT;
552+
pipelineRobustnessFeature.pNext = deviceExtensionFeatures;
553+
deviceExtensionFeatures = &pipelineRobustnessFeature;
554+
pipelineRobustnessFeature.pipelineRobustness = VK_TRUE;
555+
}
527556

528557
std::vector<const char*> used_extensions;
529558
VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions);
@@ -1127,6 +1156,8 @@ VkDeviceCreateInfo VulkanRenderer::CreateDeviceCreateInfo(const std::vector<VkDe
11271156
used_extensions.emplace_back(VK_KHR_PRESENT_ID_EXTENSION_NAME);
11281157
used_extensions.emplace_back(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
11291158
}
1159+
if (m_featureControl.deviceExtensions.pipeline_robustness)
1160+
used_extensions.emplace_back(VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME);
11301161

11311162
VkDeviceCreateInfo createInfo{};
11321163
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
@@ -1224,6 +1255,7 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
12241255
info.deviceExtensions.shader_float_controls = isExtensionAvailable(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
12251256
info.deviceExtensions.dynamic_rendering = false; // isExtensionAvailable(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
12261257
info.deviceExtensions.depth_clip_enable = isExtensionAvailable(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
1258+
info.deviceExtensions.pipeline_robustness = isExtensionAvailable(VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME);
12271259
// dynamic rendering doesn't provide any benefits for us right now. Driver implementations are very unoptimized as of Feb 2022
12281260
info.deviceExtensions.present_wait = isExtensionAvailable(VK_KHR_PRESENT_WAIT_EXTENSION_NAME) && isExtensionAvailable(VK_KHR_PRESENT_ID_EXTENSION_NAME);
12291261

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ class VulkanRenderer : public Renderer
453453
bool shader_float_controls = false; // VK_KHR_shader_float_controls
454454
bool present_wait = false; // VK_KHR_present_wait
455455
bool depth_clip_enable = false; // VK_EXT_depth_clip_enable
456+
bool pipeline_robustness = false; // VK_EXT_pipeline_robustness
456457
}deviceExtensions;
457458

458459
struct

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ PipelineInfo* VulkanRenderer::draw_createGraphicsPipeline(uint32 indexCount)
298298
// init pipeline compiler
299299
PipelineCompiler* pipelineCompiler = new PipelineCompiler();
300300

301-
pipelineCompiler->InitFromCurrentGPUState(pipelineInfo, LatteGPUState.contextNew, vkFBO->GetRenderPassObj());
301+
bool requiresRobustBufferAccess = PipelineCompiler::CalcRobustBufferAccessRequirement(vertexShader, pixelShader, geometryShader);
302+
pipelineCompiler->InitFromCurrentGPUState(pipelineInfo, LatteGPUState.contextNew, vkFBO->GetRenderPassObj(), requiresRobustBufferAccess);
302303
pipelineCompiler->TrackAsCached(vsBaseHash, pipelineHash);
303304

304305
// use heuristics based on parameter patterns to determine if the current drawcall is essential (non-skipable)

0 commit comments

Comments
 (0)