Merge VSync and VRR fixes for combined testing

This commit is contained in:
lucannez64 committed 2026-10-19 12:15:09 +02:00
commit 6bfdfbcac7
6 files changed
+265 -42

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
"""Exercise the patched presentation policy and CGL call order without a GPU."""
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
import test_runtime_compatibility as sources
@unittest.skipUnless(shutil.which('c++'), 'C++ compiler required')
class MetalVSyncChecks(unittest.TestCase):
patched = sources.CompatibilityChecks.patched
def test_explicit_vsync_is_scoped_to_each_cgl_context(self):
source = self.patched('darling', 'src/frameworks/OpenGL/OpenGL.c')
methods = '\n'.join(sources.function(source, name) for name in ('CGLFlushDrawable', 'CGLSetParameter'))
fixture = r'''
#include <cassert>
#include <cstdio>
using GLint=int;
using CGLError=int;
using CGLContextParameter=int;
using EGLBoolean=int;
constexpr int kCGLNoError=0, kCGLBadContext=1, kCGLBadState=2, kCGLBadAddress=3, kCGLBadValue=4;
constexpr int kCGLCPSwapInterval=1;
struct Context { int egl_display=1, egl_surface=1, swap_interval=1, swap_interval_requested=0; };
using CGLContextObj=Context*;
static Context* current;
static int display=1, applied=-1;
static bool fail=false;
static int swapInterval(int, int value) { if(fail) return 0; applied=value; return 1; }
static auto p_eglSwapInterval=swapInterval;
static int p_eglSwapBuffers(int,int) { return 1; }
static Context* CGLGetCurrentContext() { return current; }
''' + methods + r'''
int main() {
Context a, b;
current=&a;
int interval=1;
assert(CGLSetParameter(&a,kCGLCPSwapInterval,&interval)==0 && applied==1);
CGLFlushDrawable(&a); assert(applied==1);
current=&b;
CGLFlushDrawable(&b); assert(applied==0); // Another window retains its default.
current=&a;
interval=0;
assert(CGLSetParameter(&a,kCGLCPSwapInterval,&interval)==0 && applied==0);
interval=1;
assert(CGLSetParameter(&a,kCGLCPSwapInterval,&interval)==0 && applied==1);
current=&b; fail=true;
assert(CGLSetParameter(&b,kCGLCPSwapInterval,&interval)==kCGLBadValue);
assert(!b.swap_interval_requested); // Failed requests must not change policy.
fail=false; CGLFlushDrawable(&b); assert(applied==0);
assert(CGLFlushDrawable(nullptr)==kCGLBadContext);
}
'''
self.run_cpp(fixture)
def test_nested_layers_toggles_windows_and_context_failures(self):
source = self.patched('cocotron', 'QuartzCore/CALayerContext.m')
policy = sources.function(source, 'metalSwapInterval')
# Translate only Objective-C message/iteration syntax; compile the actual
# policy and flush body against inert layer and CGL fixtures.
policy = policy.replace('[[layer class] isSubclassOfClass: [CAMetalLayerInternal class]]', 'layer->metal')
policy = policy.replace('[(CAMetalLayer *)layer displaySyncEnabled]', 'layer->sync')
policy = policy.replace('for (CALayer *child in layer.sublayers)', 'for (CALayer *child : layer->children)')
flush = source.split('- (void) flush {', 1)[1].split('\n}\n', 1)[0]
fixture = r'''
#include <cassert>
#include <vector>
using GLint = int;
using CGLContextObj = int;
constexpr int kCGLNoError = 0, kCGLCPSwapInterval = 1;
constexpr int NULL_CONTEXT = 0;
struct CALayer { bool metal=false, sync=true; std::vector<CALayer*> children; };
struct Context { int interval=-2, swaps=0; } contexts[3];
static int current=2, attached=0;
static bool fail=false, skip=false;
static int CGLGetCurrentContext() { return current; }
static int CGLSetCurrentContext(int context) { current=context; return 0; }
static int CGLContextMakeCurrentAndAttachToWindow(int context, int window) {
if(fail) return 1;
current=context; attached=window; return 0;
}
static int CGLSetParameter(int context, int parameter, const int* interval) {
assert(current==context && attached==context && parameter==kCGLCPSwapInterval);
contexts[context].interval=*interval; return 0;
}
static int CGLFlushDrawable(int context) {
assert(current==context && attached==context);
++contexts[context].swaps; return 0;
}
static bool skipLocalRender(CALayer*, int) { return skip; }
'''
fixture += policy
fixture += '\nstruct Window { int _glContext=1, _cglWindow=1, _frame=0; CALayer* _layer; void flush() {'
fixture += flush.replace('NULL', 'NULL_CONTEXT').replace('_frame.size', '_frame') + '\n} };\n'
fixture += r'''
int main() {
CALayer root, container, metal, other;
metal.metal=other.metal=true;
root.children={&container}; container.children={&metal};
assert(metalSwapInterval(&root)==1);
Window a{1,1,0,&root};
a.flush(); assert(contexts[1].interval==1 && current==2);
metal.sync=false;
a.flush(); assert(contexts[1].interval==0 && current==2);
container.children.push_back(&other);
a.flush(); assert(contexts[1].interval==1);
other.sync=false;
a.flush(); assert(contexts[1].interval==0);
CALayer plain;
assert(metalSwapInterval(&plain)==-1);
Window b{2,2,0,&plain};
b.flush(); assert(contexts[2].interval==-2);
other.sync=true;
a.flush(); assert(contexts[1].interval==1 && contexts[2].interval==-2);
a._cglWindow=0;
int swaps=contexts[1].swaps;
a.flush(); assert(contexts[1].swaps==swaps);
a._cglWindow=1; fail=true;
a.flush(); assert(contexts[1].swaps==swaps && current==2);
fail=false; skip=true;
a.flush(); assert(contexts[1].swaps==swaps);
skip=false; current=0;
a.flush(); assert(current==0);
current=1; a.flush(); assert(current==1);
root.children.clear();
assert(metalSwapInterval(&root)==-1);
}
'''
self.run_cpp(fixture)
def run_cpp(self, fixture):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory)
(path/'check.cpp').write_text(fixture)
result = subprocess.run(['c++', '-std=c++17', str(path/'check.cpp'), '-o', str(path/'check')], capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
subprocess.run([str(path/'check')], check=True, timeout=10)