From 6d5d04ff711b7ef77f6f2f2eb72ecce18be5a48c Mon Sep 17 00:00:00 2001 From: lucannez64 <58551020+lucannez64@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:16:53 +0200 Subject: [PATCH] Use optimal tiling for the drawable GL import on NVIDIA NVIDIA's GL rejects GL_LINEAR_TILING_EXT with GL_INVALID_VALUE, so the linear internal image could not be imported and the window stayed black. Pick the tiling by GL_VENDOR; other drivers keep linear. Co-Authored-By: Claude Sonnet 5.5 --- docs/TROUBLESHOOTING.md | 4 ++-- patches/cocotron.patch | 52 ++++++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index d1bd865..fdec02e 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -212,11 +212,11 @@ Resources only the GPU uses (`private`, `memoryless`, and drawables, including L The first import of a drawable into the presenting OpenGL context, and the first semaphore import, are logged too; later ones only when they fail (at most five): ``` -[MetalDevice] pid=… drawable GL import (first) dedicated=yes size=… 1920x1080 memoryType=1 tiling=linear glTiling=linear dedicated_error=0x0 import_error=0x0 setup_error=0x0 storage_error=0x0 query_error=0x0 ok +[MetalDevice] pid=… drawable GL import (first) dedicated=yes size=… 1920x1080 memoryType=1 vkTiling=linear glTiling=linear dedicated_error=0x0 import_error=0x0 setup_error=0x0 storage_error=0x0 query_error=0x0 ok [MetalDevice] pid=… drawable GL semaphore (first) import_error=0x0 wait_error=0x0 ok (checked for the first 600 imports) ``` -A non-zero error or `FAILED` means the frames cannot reach the window. Before 2026-10-16 the import did not mark the memory as dedicated, which the extension requires; Mesa ignores it, and it is the suspected cause of the NVIDIA black screen (not yet confirmed). If the game shows a black screen on NVIDIA, report it with this file. +A non-zero error or `FAILED` means the frames cannot reach the window. Before 2026-10-16 the import did not mark the memory as dedicated, which the extension requires; Mesa ignores it, and it is the suspected cause of the NVIDIA black screen (not yet confirmed). An NVIDIA log of 2026-10-16 showed `glTiling=optimal` with `setup_error=0x501 storage_error=0x501 FAILED`: NVIDIA's GL rejects `GL_LINEAR_TILING_EXT`, so the linear Vulkan image could not be imported. Since then the internal image and the GL texture use optimal tiling when `GL_VENDOR` contains NVIDIA (`vkTiling=optimal glTiling=optimal ok` is the expected line); everything else keeps linear. If the game shows a black screen on NVIDIA, report it with this file. ### Host-side checks diff --git a/patches/cocotron.patch b/patches/cocotron.patch index e06956b..04da854 100644 --- a/patches/cocotron.patch +++ b/patches/cocotron.patch @@ -7131,7 +7131,7 @@ index 5ad375f8..c8e83814 100644 +- (BOOL) _treeHasAnimations; @end diff --git a/QuartzCore/CAMetalDrawable.mm b/QuartzCore/CAMetalDrawable.mm -index d6f86b31..20e4e88b 100644 +index d6f86b31..687f05d5 100644 --- a/QuartzCore/CAMetalDrawable.mm +++ b/QuartzCore/CAMetalDrawable.mm @@ -31,6 +31,13 @@ @@ -7148,7 +7148,7 @@ index d6f86b31..20e4e88b 100644 namespace DynamicVK = Indium::DynamicVK; static void reportGLErrors(void) { -@@ -50,8 +57,98 @@ static void reportGLErrors(void) { +@@ -50,8 +57,111 @@ static void reportGLErrors(void) { namespace Indium::DynamicVK { static DynamicFunction vkGetSemaphoreFdKHR("vkGetSemaphoreFdKHR"); static DynamicFunction vkGetMemoryFdKHR("vkGetMemoryFdKHR"); @@ -7243,11 +7243,24 @@ index d6f86b31..20e4e88b 100644 + appendDeviceLog(line); + }); +} ++ ++// The internal image is shared with OpenGL as linear memory because of a Mesa bug with ++// optimal tiling on AMD. NVIDIA's proprietary GL rejects GL_LINEAR_TILING_EXT ++// (GL_INVALID_VALUE), leaving the texture optimal and its storage import failing, which ++// shows as a black window. Only NVIDIA's own driver takes the optimal path; the check ++// runs once, with the presenting GL context current. ++static bool interopUsesLinearTiling() { ++ static const bool linear = []() { ++ const GLubyte* vendor = glGetString(GL_VENDOR); ++ return !(vendor && strstr(reinterpret_cast(vendor), "NVIDIA")); ++ }(); ++ return linear; ++} + // // helper classes // -@@ -129,27 +226,109 @@ public: +@@ -129,27 +239,109 @@ public: // texture // @@ -7367,7 +7380,7 @@ index d6f86b31..20e4e88b 100644 } return targetIndex; -@@ -161,6 +340,11 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat +@@ -161,6 +353,11 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat _pixelFormat(pixelFormat), _framebufferOnly(framebufferOnly) { @@ -7379,7 +7392,16 @@ index d6f86b31..20e4e88b 100644 // // create the images // -@@ -219,7 +403,7 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat +@@ -195,7 +392,7 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat + // some drivers (e.g. AMD's drivers) don't play nice when sharing + // Vulkan images and OpenGL textures with optimal tiling + // (https://gitlab.freedesktop.org/mesa/mesa/-/issues/7657) +- imgInfo.tiling = VK_IMAGE_TILING_LINEAR; ++ imgInfo.tiling = interopUsesLinearTiling() ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + + VkExternalMemoryImageCreateInfo extMemInfo {}; + +@@ -219,7 +416,7 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat DynamicVK::vkGetImageMemoryRequirements(_device->device(), _image, &reqs); @@ -7388,7 +7410,7 @@ index d6f86b31..20e4e88b 100644 if (targetIndex == SIZE_MAX) { throw std::runtime_error("No suitable memory region found for image"); -@@ -242,7 +426,7 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat +@@ -242,7 +439,7 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat DynamicVK::vkGetImageMemoryRequirements(_device->device(), _internalImage, &reqs); @@ -7397,7 +7419,7 @@ index d6f86b31..20e4e88b 100644 if (targetIndex == SIZE_MAX) { throw std::runtime_error("No suitable memory region found for internal image"); -@@ -380,16 +564,26 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat +@@ -380,31 +577,70 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat abort(); } @@ -7426,7 +7448,14 @@ index d6f86b31..20e4e88b 100644 GLint prevTex = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTex); -@@ -401,10 +595,36 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat + + glCreateTextures(GL_TEXTURE_2D, 1, &_textureID); + reportGLErrors(); +- glTextureParameteri(_textureID, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT /*GL_OPTIMAL_TILING_EXT*/); ++ if (interopUsesLinearTiling()) { ++ glTextureParameteri(_textureID, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT); ++ } + glTextureParameteri(_textureID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTextureParameteri(_textureID, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTextureParameteri(_textureID, GL_TEXTURE_WRAP_S, GL_REPEAT); glTextureParameteri(_textureID, GL_TEXTURE_WRAP_T, GL_REPEAT); @@ -7454,10 +7483,11 @@ index d6f86b31..20e4e88b 100644 + glGetTextureParameteriv(_textureID, GL_TEXTURE_TILING_EXT, &tiling); + const GLenum queryError = glGetError(); + char line[512]; -+ snprintf(line, sizeof(line), "drawable GL import%s dedicated=%s size=%llu %dx%d memoryType=%zu tiling=linear glTiling=%s" ++ snprintf(line, sizeof(line), "drawable GL import%s dedicated=%s size=%llu %dx%d memoryType=%zu vkTiling=%s glTiling=%s" + " dedicated_error=0x%x import_error=0x%x setup_error=0x%x storage_error=0x%x query_error=0x%x%s", + first ? " (first)" : "", dedicatedReadBack == GL_TRUE ? "yes" : (dedicatedReadBack == -1 ? "unknown" : "no"), + (unsigned long long) reqs.size, (int) _size.width, (int) _size.height, targetIndex, ++ interopUsesLinearTiling() ? "linear" : "optimal", + tiling == GL_LINEAR_TILING_EXT ? "linear" : (tiling == GL_OPTIMAL_TILING_EXT ? "optimal" : "unknown"), + dedicatedError, importError, setupError, storageError, queryError, failed ? " FAILED" : " ok"); + appendDeviceLog(line); @@ -7465,7 +7495,7 @@ index d6f86b31..20e4e88b 100644 glBindTexture(GL_TEXTURE_2D, prevTex); }; -@@ -605,35 +825,16 @@ void CAMetalDrawableActual::present() { +@@ -605,35 +841,16 @@ void CAMetalDrawableActual::present() { return; } @@ -7507,7 +7537,7 @@ index d6f86b31..20e4e88b 100644 } } -@@ -702,9 +903,46 @@ void CAMetalDrawableActual::reset() { +@@ -702,9 +919,46 @@ void CAMetalDrawableActual::reset() { }; void CAMetalDrawableActual::synchronizeRender() {