diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 715ddd90..7a8155f9 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -5771,6 +5771,10 @@ namespace MobileGL::MG_Backend::DirectGLES { // Either way a reference taken by the first call is stale by the time the second returns, // and it is read four more times below. Copying the SharedPtr costs two refcount bumps on // a path that is already doing a texture copy. + // An endpoint that named nothing is the frontend validator's INVALID_VALUE and never + // reaches here - but the assertion that says so is compiled out of a release build, and + // SyncTextureObjectToBackend would register a null state object. + if (!endpoint.Texture) return false; out.texture = TextureImpl::SyncTextureObjectToBackend(endpoint.Texture); if (!out.texture) return false; const TextureTarget stateTarget = MG_Util::ConvertGLEnumToTextureTarget(appTarget); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 2de8c9d0..6d2dbf7d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -9007,6 +9007,9 @@ void main() { out.arrayLayers = 1; return out.image != VK_NULL_HANDLE; } + // An endpoint that named nothing is the frontend validator's INVALID_VALUE and never + // reaches here - but the assertion that says so is compiled out of a release build. + if (endpoint.Texture == nullptr) return false; auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*endpoint.Texture); if (resource == nullptr) return false; out.isRenderbuffer = false; @@ -9127,16 +9130,23 @@ void main() { // undefined by the same spec sentence that lets the application ask. Both sides therefore // take the same shape - transition the whole image out of UNDEFINED and settle it on a // real layout afterwards, since UNDEFINED is not a layout a barrier may transition BACK to. - const auto resolveRestoreLayout = [copyAspectMask](VkImageLayout originalLayout) { + // A renderbuffer settles on its ATTACHMENT layout instead: it is never sampled, and that is + // the layout MaterializePendingClearForRenderbuffer leaves it in. + const auto resolveRestoreLayout = [copyAspectMask](VkImageLayout originalLayout, Bool isRenderbuffer) { if (originalLayout != VK_IMAGE_LAYOUT_UNDEFINED) { return originalLayout; } - return (copyAspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0 - ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL - : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + const Bool depthStencil = + (copyAspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0; + if (isRenderbuffer) { + return depthStencil ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + return depthStencil ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL + : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; }; - const VkImageLayout srcRestoreLayout = resolveRestoreLayout(srcOriginalLayout); - const VkImageLayout dstRestoreLayout = resolveRestoreLayout(dstOriginalLayout); + const VkImageLayout srcRestoreLayout = resolveRestoreLayout(srcOriginalLayout, srcImage.isRenderbuffer); + const VkImageLayout dstRestoreLayout = resolveRestoreLayout(dstOriginalLayout, dstImage.isRenderbuffer); VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkAccessFlags srcAccessMask = 0;