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 <[email protected]>
This commit is contained in:
lucannez64andClaude Sonnet 5.5 committed 2026-10-16 11:16:53 +02:00
1 parent 0b555fa54d
commit 6d5d04ff71
2 files changed
+43 -13

No files matched your search

+2 -2
View File
@@ -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): 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) [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 ### Host-side checks
+41 -11
View File
@@ -7131,7 +7131,7 @@ index 5ad375f8..c8e83814 100644
+- (BOOL) _treeHasAnimations; +- (BOOL) _treeHasAnimations;
@end @end
diff --git a/QuartzCore/CAMetalDrawable.mm b/QuartzCore/CAMetalDrawable.mm diff --git a/QuartzCore/CAMetalDrawable.mm b/QuartzCore/CAMetalDrawable.mm
index d6f86b31..20e4e88b 100644 index d6f86b31..687f05d5 100644
--- a/QuartzCore/CAMetalDrawable.mm --- a/QuartzCore/CAMetalDrawable.mm
+++ b/QuartzCore/CAMetalDrawable.mm +++ b/QuartzCore/CAMetalDrawable.mm
@@ -31,6 +31,13 @@ @@ -31,6 +31,13 @@
@@ -7148,7 +7148,7 @@ index d6f86b31..20e4e88b 100644
namespace DynamicVK = Indium::DynamicVK; namespace DynamicVK = Indium::DynamicVK;
static void reportGLErrors(void) { static void reportGLErrors(void) {
@@ -50,8 +57,98 @@ static void reportGLErrors(void) { @@ -50,8 +57,111 @@ static void reportGLErrors(void) {
namespace Indium::DynamicVK { namespace Indium::DynamicVK {
static DynamicFunction<PFN_vkGetSemaphoreFdKHR> vkGetSemaphoreFdKHR("vkGetSemaphoreFdKHR"); static DynamicFunction<PFN_vkGetSemaphoreFdKHR> vkGetSemaphoreFdKHR("vkGetSemaphoreFdKHR");
static DynamicFunction<PFN_vkGetMemoryFdKHR> vkGetMemoryFdKHR("vkGetMemoryFdKHR"); static DynamicFunction<PFN_vkGetMemoryFdKHR> vkGetMemoryFdKHR("vkGetMemoryFdKHR");
@@ -7243,11 +7243,24 @@ index d6f86b31..20e4e88b 100644
+ appendDeviceLog(line); + 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<const char*>(vendor), "NVIDIA"));
+ }();
+ return linear;
+}
+ +
// //
// helper classes // helper classes
// //
@@ -129,27 +226,109 @@ public: @@ -129,27 +239,109 @@ public:
// texture // texture
// //
@@ -7367,7 +7380,7 @@ index d6f86b31..20e4e88b 100644
} }
return targetIndex; return targetIndex;
@@ -161,6 +340,11 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat @@ -161,6 +353,11 @@ CAMetalDrawableTexture::CAMetalDrawableTexture(CGSize size, Indium::PixelFormat
_pixelFormat(pixelFormat), _pixelFormat(pixelFormat),
_framebufferOnly(framebufferOnly) _framebufferOnly(framebufferOnly)
{ {
@@ -7379,7 +7392,16 @@ index d6f86b31..20e4e88b 100644
// //
// create the images // 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); DynamicVK::vkGetImageMemoryRequirements(_device->device(), _image, &reqs);
@@ -7388,7 +7410,7 @@ index d6f86b31..20e4e88b 100644
if (targetIndex == SIZE_MAX) { if (targetIndex == SIZE_MAX) {
throw std::runtime_error("No suitable memory region found for image"); 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); DynamicVK::vkGetImageMemoryRequirements(_device->device(), _internalImage, &reqs);
@@ -7397,7 +7419,7 @@ index d6f86b31..20e4e88b 100644
if (targetIndex == SIZE_MAX) { if (targetIndex == SIZE_MAX) {
throw std::runtime_error("No suitable memory region found for internal image"); 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(); abort();
} }
@@ -7426,7 +7448,14 @@ index d6f86b31..20e4e88b 100644
GLint prevTex = 0; GLint prevTex = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTex); 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_MIN_FILTER, GL_NEAREST);
glTextureParameteri(_textureID, GL_TEXTURE_WRAP_S, GL_REPEAT); glTextureParameteri(_textureID, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTextureParameteri(_textureID, GL_TEXTURE_WRAP_T, 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); + glGetTextureParameteriv(_textureID, GL_TEXTURE_TILING_EXT, &tiling);
+ const GLenum queryError = glGetError(); + const GLenum queryError = glGetError();
+ char line[512]; + 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", + " 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"), + first ? " (first)" : "", dedicatedReadBack == GL_TRUE ? "yes" : (dedicatedReadBack == -1 ? "unknown" : "no"),
+ (unsigned long long) reqs.size, (int) _size.width, (int) _size.height, targetIndex, + (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"), + tiling == GL_LINEAR_TILING_EXT ? "linear" : (tiling == GL_OPTIMAL_TILING_EXT ? "optimal" : "unknown"),
+ dedicatedError, importError, setupError, storageError, queryError, failed ? " FAILED" : " ok"); + dedicatedError, importError, setupError, storageError, queryError, failed ? " FAILED" : " ok");
+ appendDeviceLog(line); + appendDeviceLog(line);
@@ -7465,7 +7495,7 @@ index d6f86b31..20e4e88b 100644
glBindTexture(GL_TEXTURE_2D, prevTex); glBindTexture(GL_TEXTURE_2D, prevTex);
}; };
@@ -605,35 +825,16 @@ void CAMetalDrawableActual::present() { @@ -605,35 +841,16 @@ void CAMetalDrawableActual::present() {
return; 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() { void CAMetalDrawableActual::synchronizeRender() {