mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Fix] (DirectVulkan): explicit-LOD blit sampling and present-path hardening - the default-framebuffer blit shader now samples with textureLod 0 (a blit reads exactly the selected level; Adreno 650's implicit-LOD path reads past a single-mip UBWC render target's allocation despite maxLod=0, page-faulting the GPU on MC 26.2's second startup frame once the neighbouring startup staging memory is returned - the invalidated context then failed the next Present submit with EDEADLK/DEVICE_LOST), TransitionToPresent appends the present barrier into the frame's open recording instead of silently dropping it whenever anything was recorded (frames without a default-FBO render pass presented images stuck in their acquired layout), VK_SUBOPTIMAL_KHR acquires are treated as the success they are (image acquired, semaphore signal armed - the early return skipped the fence reset and consumed-flag clear, and callers re-acquired on the same binary semaphore; rebuilds now defer to after the signal is consumed), and validation builds report through VK_EXT_debug_report when VK_EXT_debug_utils is absent instead of aborting instance creation
This commit is contained in:
@@ -150,12 +150,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) {
|
Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) {
|
||||||
auto& frame = GetCurrent();
|
auto& frame = GetCurrent();
|
||||||
if (frame.hasCommandBufferRecorded || frame.isCommandRecording || oldLayout == presentLayout ||
|
if (oldLayout == presentLayout || oldLayout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
||||||
oldLayout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& commandBuffer = BeginCommandRecording();
|
// The barrier belongs in the frame's own recording. Bailing out because
|
||||||
|
// something was already recorded (the previous behaviour) dropped the
|
||||||
|
// transition entirely for every frame that never ran a default-framebuffer
|
||||||
|
// render pass - the only other thing that carries the image to
|
||||||
|
// PRESENT_SRC_KHR, via that pass's finalLayout - so the swapchain image was
|
||||||
|
// handed to the WSI still in the layout it was acquired in.
|
||||||
|
// A closed-but-unsubmitted buffer can only come from a submit that already
|
||||||
|
// failed (SubmitPendingCommandBuffer leaves the flag set on error), and
|
||||||
|
// appending to it is illegal while reopening would reset the frame's own
|
||||||
|
// commands away. The device is gone on that path anyway - stay silent-safe
|
||||||
|
// rather than trade a lost device for a barrier into a closed buffer.
|
||||||
|
if (frame.hasCommandBufferRecorded) {
|
||||||
|
MGLOG_E("TransitionToPresent: command buffer already closed; skipping the present barrier");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopening a recording here would vkResetCommandBuffer this frame's own
|
||||||
|
// commands away, so append to the open one and let the caller close it.
|
||||||
|
const Bool openedRecording = !frame.isCommandRecording;
|
||||||
|
VkCommandBuffer commandBuffer = openedRecording ? BeginCommandRecording() : frame.commandBuffer;
|
||||||
|
|
||||||
VkImageMemoryBarrier presentBarrier{};
|
VkImageMemoryBarrier presentBarrier{};
|
||||||
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
@@ -174,7 +192,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
|
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
|
||||||
nullptr, 0, nullptr, 1, &presentBarrier);
|
nullptr, 0, nullptr, 1, &presentBarrier);
|
||||||
|
|
||||||
EndCommandRecording();
|
if (openedRecording) {
|
||||||
|
EndCommandRecording();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,12 +247,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
result = vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence,
|
result = vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence,
|
||||||
&outImageIndex);
|
&outImageIndex);
|
||||||
if (result != VK_SUCCESS) {
|
// VK_SUBOPTIMAL_KHR is a success code: an image *was* acquired and
|
||||||
|
// imageAvailableSemaphore *will* be signaled. Bailing out on it skipped both
|
||||||
|
// the consumed-flag reset (leaving a stale "already consumed", so the next
|
||||||
|
// submit never waited on the pending signal) and the fence reset (leaving
|
||||||
|
// the slot's fence signaled for the next submit to reuse). Only a genuine
|
||||||
|
// failure - VK_ERROR_OUT_OF_DATE_KHR and friends, where nothing is acquired
|
||||||
|
// and nothing is signaled - skips the bookkeeping.
|
||||||
|
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.imageAvailableSemaphoreConsumed = false;
|
frame.imageAvailableSemaphoreConsumed = false;
|
||||||
return vkResetFences(device, 1, &frame.imageInFlightFence);
|
const VkResult resetResult = vkResetFences(device, 1, &frame.imageInFlightFence);
|
||||||
|
// Hand the acquire's own code back so the caller can schedule a rebuild.
|
||||||
|
return resetResult == VK_SUCCESS ? result : resetResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 FrameContext::GetCurrentFrameIndex() const {
|
Uint32 FrameContext::GetCurrentFrameIndex() const {
|
||||||
|
|||||||
@@ -1016,7 +1016,18 @@ layout(location = 0) in vec2 vTexCoord;
|
|||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
outColor = texture(uSource, vTexCoord);
|
// Explicit LOD, not texture(): a blit reads exactly the selected level, so
|
||||||
|
// derivative-based mip selection has no business here. It is also load-bearing:
|
||||||
|
// on Adreno 650 (driver 512.502) an implicit-LOD sample of this single-mip
|
||||||
|
// UBWC render target through the pre-rotation (ROTATE_90) mapping reads past
|
||||||
|
// the image's allocation - despite the sampler's maxLod=0 and a nominal 1:1
|
||||||
|
// texel mapping whose LOD is 0, so the driver's implicit-LOD path itself is at
|
||||||
|
// fault - and page-faults the GPU once the neighbouring memory is returned to
|
||||||
|
// the kernel (frame 2 of Minecraft 26.2's resource reload; the kernel then
|
||||||
|
// invalidates the context and the next submit dies with EDEADLK ->
|
||||||
|
// VK_ERROR_DEVICE_LOST at Present). Verified on device: texture() faults on
|
||||||
|
// the second frame every run, textureLod survives with identical state.
|
||||||
|
outColor = textureLod(uSource, vTexCoord, 0.0);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
@@ -2563,11 +2574,22 @@ void main() {
|
|||||||
if (m_swapchainObject.GetHandle() != VK_NULL_HANDLE) {
|
if (m_swapchainObject.GetHandle() != VK_NULL_HANDLE) {
|
||||||
VkResult acquireResult =
|
VkResult acquireResult =
|
||||||
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR || acquireResult == VK_SUBOPTIMAL_KHR) {
|
if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
|
// Nothing was acquired and no semaphore signal was armed, so
|
||||||
|
// rebuilding and re-acquiring on the same semaphore is safe.
|
||||||
MGLOG_D("Initialize, vkAcquireNextImageKHR got %d, recreating swapchain", acquireResult);
|
MGLOG_D("Initialize, vkAcquireNextImageKHR got %d, recreating swapchain", acquireResult);
|
||||||
RecreateSwapchain();
|
RecreateSwapchain();
|
||||||
acquireResult =
|
acquireResult =
|
||||||
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
|
} else if (acquireResult == VK_SUBOPTIMAL_KHR) {
|
||||||
|
// The image is usable, and its acquire signal is already armed on
|
||||||
|
// imageAvailableSemaphore. Re-acquiring here would arm a second
|
||||||
|
// signal on a binary semaphore whose first one nobody has waited on
|
||||||
|
// yet; keep the image and let Present rebuild after the frame that
|
||||||
|
// consumes the signal.
|
||||||
|
MGLOG_D("Initialize, vkAcquireNextImageKHR got VK_SUBOPTIMAL_KHR; deferring swapchain rebuild");
|
||||||
|
m_swapchainResizeRequested = true;
|
||||||
|
acquireResult = VK_SUCCESS;
|
||||||
}
|
}
|
||||||
VK_VERIFY(acquireResult, "Initialize, WaitAndAcquireNextImage");
|
VK_VERIFY(acquireResult, "Initialize, WaitAndAcquireNextImage");
|
||||||
} else {
|
} else {
|
||||||
@@ -2693,6 +2715,7 @@ void main() {
|
|||||||
DestroyDebugMessenger();
|
DestroyDebugMessenger();
|
||||||
m_debugMessenger = VK_NULL_HANDLE;
|
m_debugMessenger = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
DestroyDebugReportCallback();
|
||||||
|
|
||||||
if (m_instance != VK_NULL_HANDLE) {
|
if (m_instance != VK_NULL_HANDLE) {
|
||||||
vkDestroyInstance(m_instance, nullptr);
|
vkDestroyInstance(m_instance, nullptr);
|
||||||
@@ -7542,7 +7565,11 @@ void main() {
|
|||||||
m_presentSuspended = false;
|
m_presentSuspended = false;
|
||||||
const VkResult acquireResult =
|
const VkResult acquireResult =
|
||||||
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
if (acquireResult != VK_SUBOPTIMAL_KHR) {
|
if (acquireResult == VK_SUBOPTIMAL_KHR) {
|
||||||
|
// Usable image with its acquire signal already armed; rebuild only
|
||||||
|
// once a submit has consumed it (see step 4 at the end of Present).
|
||||||
|
m_swapchainResizeRequested = true;
|
||||||
|
} else {
|
||||||
VK_VERIFY(acquireResult, "Present, deferred first WaitAndAcquireNextImage");
|
VK_VERIFY(acquireResult, "Present, deferred first WaitAndAcquireNextImage");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7570,16 +7597,24 @@ void main() {
|
|||||||
if (activeRenderPass)
|
if (activeRenderPass)
|
||||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
|
|
||||||
|
// Transition while this frame's recording is still open. A frame that
|
||||||
|
// rendered only into FBOs has no default-framebuffer render pass, and that
|
||||||
|
// pass's finalLayout is the only other thing that carries the swapchain
|
||||||
|
// image to PRESENT_SRC_KHR - so closing the buffer first, which made
|
||||||
|
// TransitionToPresent refuse to record, handed the image to
|
||||||
|
// vkQueuePresentKHR in the layout it was acquired in (UNDEFINED on a fresh
|
||||||
|
// swapchain). The SetImageLayout below then made the tracker's
|
||||||
|
// disagreement with reality permanent for that image index.
|
||||||
|
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||||
|
m_frameContext.TransitionToPresent(m_swapchainObject.GetImage(m_imageIndexAcquired), acquiredImageLayout);
|
||||||
|
|
||||||
if (frame.isCommandRecording) {
|
if (frame.isCommandRecording) {
|
||||||
m_frameContext.EndCommandRecording();
|
m_frameContext.EndCommandRecording();
|
||||||
frame.hasCommandBufferRecorded = true;
|
frame.hasCommandBufferRecorded = true;
|
||||||
m_lastPipelineValid = false; // command-buffer boundary: drop the pipeline memo
|
m_lastPipelineValid = false; // command-buffer boundary: drop the pipeline memo
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
const Bool shouldSubmitCommandBuffer = frame.hasCommandBufferRecorded;
|
||||||
const Bool needsLayoutTransitionForPresent =
|
|
||||||
m_frameContext.TransitionToPresent(m_swapchainObject.GetImage(m_imageIndexAcquired), acquiredImageLayout);
|
|
||||||
const Bool shouldSubmitCommandBuffer = frame.hasCommandBufferRecorded || needsLayoutTransitionForPresent;
|
|
||||||
|
|
||||||
// 1) Submit current frame work.
|
// 1) Submit current frame work.
|
||||||
auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer, m_imageIndexAcquired);
|
auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer, m_imageIndexAcquired);
|
||||||
@@ -7623,7 +7658,17 @@ void main() {
|
|||||||
|
|
||||||
// 4) Wait/reset/acquire for next frame.
|
// 4) Wait/reset/acquire for next frame.
|
||||||
result = m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
result = m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_SUBOPTIMAL_KHR) {
|
||||||
|
// An image was acquired and its signal is armed on this slot's
|
||||||
|
// imageAvailableSemaphore. Rebuilding now would mean re-acquiring on
|
||||||
|
// that same binary semaphore, arming a second signal while the first is
|
||||||
|
// still unwaited. Keep the image: the rebuild runs at the top of the
|
||||||
|
// next Present's step 4 above, once this frame's submit has consumed it.
|
||||||
|
MGLOG_D("Present, vkAcquireNextImageKHR got VK_SUBOPTIMAL_KHR; deferring swapchain rebuild");
|
||||||
|
m_swapchainResizeRequested = true;
|
||||||
|
result = VK_SUCCESS;
|
||||||
|
} else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
|
// Nothing acquired, nothing signaled: safe to rebuild and re-acquire.
|
||||||
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
||||||
if (!RecreateSwapchain()) {
|
if (!RecreateSwapchain()) {
|
||||||
m_presentSuspended = true;
|
m_presentSuspended = true;
|
||||||
@@ -7671,6 +7716,24 @@ void main() {
|
|||||||
|
|
||||||
m_validationLayersEnabled = m_config.EnableValidationLayers && validationLayerAvailable;
|
m_validationLayersEnabled = m_config.EnableValidationLayers && validationLayerAvailable;
|
||||||
|
|
||||||
|
// The debug messenger is a VK_EXT_debug_utils object, but a driver can ship
|
||||||
|
// the validation layers while exposing only the older VK_EXT_debug_report
|
||||||
|
// (Adreno 650 / Vulkan 1.1.128 does exactly that). Requesting the extension
|
||||||
|
// unconditionally tripped the required-extension assert below, aborting every
|
||||||
|
// validation-enabled build in CreateInstance. Keep the layers - they still
|
||||||
|
// validate, and on Android they report to logcat on their own - and drop only
|
||||||
|
// the messenger.
|
||||||
|
const Bool debugUtilsAvailable =
|
||||||
|
m_validationLayersEnabled && IsExtensionSupported(m_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
// Without a reporting channel the layers validate but say nothing, so fall
|
||||||
|
// back to VK_EXT_debug_report when debug_utils is missing.
|
||||||
|
const Bool debugReportAvailable = m_validationLayersEnabled && !debugUtilsAvailable &&
|
||||||
|
IsExtensionSupported(m_extensions, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||||
|
if (m_validationLayersEnabled && !debugUtilsAvailable) {
|
||||||
|
MGLOG_I("%s not available; validation reports via %s instead.", VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||||
|
debugReportAvailable ? VK_EXT_DEBUG_REPORT_EXTENSION_NAME : "(no channel)");
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------- App info -------------------
|
// ---------------- App info -------------------
|
||||||
VkApplicationInfo appInfo = {};
|
VkApplicationInfo appInfo = {};
|
||||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
@@ -7721,8 +7784,10 @@ void main() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_validationLayersEnabled) {
|
if (debugUtilsAvailable) {
|
||||||
exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
} else if (debugReportAvailable) {
|
||||||
|
exts.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
MGLOG_I("Enabling %d Vulkan instance extensions:", exts.size());
|
MGLOG_I("Enabling %d Vulkan instance extensions:", exts.size());
|
||||||
@@ -7747,7 +7812,8 @@ void main() {
|
|||||||
MGLOG_I("Enabling validation layer...");
|
MGLOG_I("Enabling validation layer...");
|
||||||
instanceInfo.enabledLayerCount = static_cast<uint32_t>(std::size(s_validationLayerNames));
|
instanceInfo.enabledLayerCount = static_cast<uint32_t>(std::size(s_validationLayerNames));
|
||||||
instanceInfo.ppEnabledLayerNames = s_validationLayerNames;
|
instanceInfo.ppEnabledLayerNames = s_validationLayerNames;
|
||||||
instanceInfo.pNext = &debugMessengerCreateInfo;
|
// Chaining the messenger create-info is only legal with the extension on.
|
||||||
|
instanceInfo.pNext = debugUtilsAvailable ? &debugMessengerCreateInfo : nullptr;
|
||||||
} else {
|
} else {
|
||||||
instanceInfo.enabledLayerCount = 0;
|
instanceInfo.enabledLayerCount = 0;
|
||||||
instanceInfo.pNext = nullptr;
|
instanceInfo.pNext = nullptr;
|
||||||
@@ -7755,7 +7821,40 @@ void main() {
|
|||||||
|
|
||||||
VK_VERIFY(vkCreateInstance(&instanceInfo, nullptr, &m_instance), "vkCreateInstance failed");
|
VK_VERIFY(vkCreateInstance(&instanceInfo, nullptr, &m_instance), "vkCreateInstance failed");
|
||||||
|
|
||||||
if (m_validationLayersEnabled) VK_VERIFY(SetupDebugMessenger());
|
if (debugUtilsAvailable) {
|
||||||
|
VK_VERIFY(SetupDebugMessenger());
|
||||||
|
} else if (debugReportAvailable) {
|
||||||
|
VK_VERIFY(SetupDebugReportCallback());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT,
|
||||||
|
Uint64, size_t, Int32 messageCode, const char* pLayerPrefix,
|
||||||
|
const char* pMessage, void*) {
|
||||||
|
if ((flags & (VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
|
||||||
|
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)) != 0) {
|
||||||
|
MGLOG_F("[Vulkan %s %d] %s", pLayerPrefix ? pLayerPrefix : "?", messageCode, pMessage ? pMessage : "");
|
||||||
|
}
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult VulkanRenderer::SetupDebugReportCallback() {
|
||||||
|
auto vkCreateDebugReportCallbackEXT =
|
||||||
|
(PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT");
|
||||||
|
if (!vkCreateDebugReportCallbackEXT) return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
VkDebugReportCallbackCreateInfoEXT createInfo{VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT};
|
||||||
|
createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
|
||||||
|
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
|
||||||
|
createInfo.pfnCallback = &DebugReportCallback;
|
||||||
|
return vkCreateDebugReportCallbackEXT(m_instance, &createInfo, nullptr, &m_debugReportCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::DestroyDebugReportCallback() {
|
||||||
|
if (m_debugReportCallback == VK_NULL_HANDLE) return;
|
||||||
|
auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(m_instance,
|
||||||
|
"vkDestroyDebugReportCallbackEXT");
|
||||||
|
if (func != nullptr) func(m_instance, m_debugReportCallback, nullptr);
|
||||||
|
m_debugReportCallback = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkResult VulkanRenderer::SetupDebugMessenger() {
|
VkResult VulkanRenderer::SetupDebugMessenger() {
|
||||||
|
|||||||
@@ -399,6 +399,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Vector<VkExtensionProperties> m_extensions;
|
Vector<VkExtensionProperties> m_extensions;
|
||||||
VkInstance m_instance = VK_NULL_HANDLE;
|
VkInstance m_instance = VK_NULL_HANDLE;
|
||||||
VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE;
|
VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE;
|
||||||
|
// Fallback reporting channel for drivers that ship the validation layers but
|
||||||
|
// only expose the older VK_EXT_debug_report (Adreno 650 / Vulkan 1.1.128).
|
||||||
|
VkDebugReportCallbackEXT m_debugReportCallback = VK_NULL_HANDLE;
|
||||||
PhysicalDevice m_physicalDevice;
|
PhysicalDevice m_physicalDevice;
|
||||||
VkDevice m_device = VK_NULL_HANDLE;
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
VmaAllocator m_allocator = nullptr;
|
VmaAllocator m_allocator = nullptr;
|
||||||
@@ -549,6 +552,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void CreateInstance();
|
void CreateInstance();
|
||||||
VkResult SetupDebugMessenger();
|
VkResult SetupDebugMessenger();
|
||||||
VkResult DestroyDebugMessenger();
|
VkResult DestroyDebugMessenger();
|
||||||
|
VkResult SetupDebugReportCallback();
|
||||||
|
void DestroyDebugReportCallback();
|
||||||
VkDebugUtilsMessengerCreateInfoEXT PopulateDebugMessengerCreateInfo();
|
VkDebugUtilsMessengerCreateInfoEXT PopulateDebugMessengerCreateInfo();
|
||||||
void CreateSurface();
|
void CreateSurface();
|
||||||
void PickPhysicalDevice();
|
void PickPhysicalDevice();
|
||||||
|
|||||||
Reference in New Issue
Block a user