Last active
November 17, 2024 08:42
-
-
Save Akemi/185d83afbef47c0c8e0add2a3c99b6d2 to your computer and use it in GitHub Desktop.
macOS EDR Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
import OpenGL.GL | |
import OpenGL.GL3 | |
extension NSDeviceDescriptionKey { | |
static let screenNumber = NSDeviceDescriptionKey("NSScreenNumber") | |
} | |
extension NSScreen { | |
public var displayID: CGDirectDisplayID { | |
get { | |
return deviceDescription[.screenNumber] as? CGDirectDisplayID ?? 0 | |
} | |
} | |
public var displayName: String? { | |
get { | |
var name: String? = nil | |
var object: io_object_t | |
var iter = io_iterator_t() | |
let matching = IOServiceMatching("IODisplayConnect") | |
let result = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter) | |
if result != KERN_SUCCESS || iter == 0 { return nil } | |
repeat { | |
object = IOIteratorNext(iter) | |
if let info = IODisplayCreateInfoDictionary(object, IOOptionBits(kIODisplayOnlyPreferredName)).takeRetainedValue() as? [String:AnyObject], | |
(info[kDisplayVendorID] as? UInt32 == CGDisplayVendorNumber(displayID) && | |
info[kDisplayProductID] as? UInt32 == CGDisplayModelNumber(displayID) && | |
info[kDisplaySerialNumber] as? UInt32 ?? 0 == CGDisplaySerialNumber(displayID)) | |
{ | |
if let productNames = info["DisplayProductName"] as? [String:String], | |
let productName = productNames.first?.value | |
{ | |
name = productName | |
break | |
} | |
} | |
} while object != 0 | |
IOObjectRelease(iter) | |
return name | |
} | |
} | |
} | |
let glVersions: [CGLOpenGLProfile] = [ | |
kCGLOGLPVersion_3_2_Core, | |
kCGLOGLPVersion_Legacy | |
] | |
let glFormatBase: [CGLPixelFormatAttribute] = [ | |
kCGLPFAOpenGLProfile, | |
kCGLPFAAccelerated, | |
kCGLPFADoubleBuffer | |
] | |
let glFormatSoftwareBase: [CGLPixelFormatAttribute] = [ | |
kCGLPFAOpenGLProfile, | |
kCGLPFARendererID, | |
CGLPixelFormatAttribute(UInt32(kCGLRendererGenericFloatID)), | |
kCGLPFADoubleBuffer | |
] | |
let glFormatOptional: [[CGLPixelFormatAttribute]] = [ | |
[kCGLPFABackingStore], | |
[kCGLPFAAllowOfflineRenderers], | |
[kCGLPFASupportsAutomaticGraphicsSwitching] | |
] | |
let glFormat10Bit: [CGLPixelFormatAttribute] = [ | |
kCGLPFAColorSize, | |
_CGLPixelFormatAttribute(rawValue: 64), | |
kCGLPFAColorFloat | |
] | |
let attributeLookUp: [UInt32:String] = [ | |
kCGLOGLPVersion_3_2_Core.rawValue: "kCGLOGLPVersion_3_2_Core", | |
kCGLOGLPVersion_Legacy.rawValue: "kCGLOGLPVersion_Legacy", | |
kCGLPFAOpenGLProfile.rawValue: "kCGLPFAOpenGLProfile", | |
UInt32(kCGLRendererGenericFloatID): "kCGLRendererGenericFloatID", | |
kCGLPFARendererID.rawValue: "kCGLPFARendererID", | |
kCGLPFAAccelerated.rawValue: "kCGLPFAAccelerated", | |
kCGLPFADoubleBuffer.rawValue: "kCGLPFADoubleBuffer", | |
kCGLPFABackingStore.rawValue: "kCGLPFABackingStore", | |
kCGLPFAColorSize.rawValue: "kCGLPFAColorSize", | |
kCGLPFAColorFloat.rawValue: "kCGLPFAColorFloat", | |
kCGLPFAAllowOfflineRenderers.rawValue: "kCGLPFAAllowOfflineRenderers", | |
kCGLPFASupportsAutomaticGraphicsSwitching.rawValue: "kCGLPFASupportsAutomaticGraphicsSwitching", | |
] | |
class Layer: CAOpenGLLayer { | |
let view: View | |
var vbo: GLuint = 0 | |
var vao: GLuint = 0 | |
let vertexShaderSource: NSString = """ | |
#version 330 core | |
layout (location = 0) in vec2 position; | |
layout (location = 1) in vec3 color; | |
out vec4 fragColor; | |
void main() | |
{ | |
gl_Position = vec4(position.x, position.y, 1.0, 1.0); | |
fragColor = vec4(color.x, color.y, color.z, 1.0f); | |
} | |
""" | |
let fragmentShaderSource: NSString = """ | |
#version 330 core | |
in vec4 fragColor; | |
out vec4 color; | |
void main() | |
{ | |
color = fragColor; | |
} | |
""" | |
let vertices: [GLfloat] = [ | |
// top left white rectangle | |
// two values position | |
// three values color | |
-1.0, 1.0, | |
1.0, 1.0, 1.0, | |
0.0, 1.0, | |
1.0, 1.0, 1.0, | |
0.0, 0.0, | |
1.0, 1.0, 1.0, | |
-1.0, 1.0, | |
1.0, 1.0, 1.0, | |
-1.0, 0.0, | |
1.0, 1.0, 1.0, | |
0.0, 0.0, | |
1.0, 1.0, 1.0, | |
// bottom left ultra white rectangle | |
-1.0, 0.0, | |
2.0, 2.0, 2.0, | |
0.0, 0.0, | |
2.0, 2.0, 2.0, | |
0.0, -1.0, | |
2.0, 2.0, 2.0, | |
-1.0, 0.0, | |
2.0, 2.0, 2.0, | |
-1.0, -1.0, | |
2.0, 2.0, 2.0, | |
0.0, -1.0, | |
2.0, 2.0, 2.0, | |
// bottom right 50% gray rectangle | |
0.0, 0.0, | |
0.5, 0.5, 0.5, | |
1.0, 0.0, | |
0.5, 0.5, 0.5, | |
1.0, -1.0, | |
0.5, 0.5, 0.5, | |
0.0, 0.0, | |
0.5, 0.5, 0.5, | |
0.0, -1.0, | |
0.5, 0.5, 0.5, | |
1.0, -1.0, | |
0.5, 0.5, 0.5, | |
] | |
let verticesStride: Int = 2 | |
let colorStride: Int = 3 | |
var dataStride: Int { get { return verticesStride + colorStride } } | |
init(_ vview: View, _ edr: Bool = false) { | |
view = vview | |
super.init() | |
autoresizingMask = [.layerWidthSizable, .layerHeightSizable] | |
backgroundColor = NSColor.black.cgColor | |
wantsExtendedDynamicRangeContent = edr | |
isAsynchronous = true | |
if #available(macOS 10.12, *) { | |
contentsFormat = .RGBA16Float | |
} | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
override func canDraw(inCGLContext ctx: CGLContextObj, | |
pixelFormat pf: CGLPixelFormatObj, | |
forLayerTime t: CFTimeInterval, | |
displayTime ts: UnsafePointer<CVTimeStamp>?) -> Bool { | |
return true | |
} | |
override func draw(inCGLContext ctx: CGLContextObj, | |
pixelFormat pf: CGLPixelFormatObj, | |
forLayerTime t: CFTimeInterval, | |
displayTime ts: UnsafePointer<CVTimeStamp>?) { | |
CGLSetCurrentContext(ctx); | |
// create shaders | |
let vertexShader = glCreateShader(GLenum(GL_VERTEX_SHADER)) | |
var vxsCS = vertexShaderSource.utf8String | |
glShaderSource(vertexShader, 1, &vxsCS, nil) | |
glCompileShader(vertexShader) | |
checkShaderError(vertexShader) | |
let fragmentShader = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) | |
var fsCS = fragmentShaderSource.utf8String | |
glShaderSource(fragmentShader, 1, &fsCS, nil) | |
glCompileShader(fragmentShader) | |
checkShaderError(fragmentShader) | |
// attach shaders to program | |
let shaderProgram = glCreateProgram() | |
defer { glDeleteProgram(shaderProgram) } | |
glAttachShader(shaderProgram, vertexShader) | |
glAttachShader(shaderProgram, fragmentShader) | |
glLinkProgram(shaderProgram) | |
checkProgramError(shaderProgram) | |
glDeleteShader(vertexShader) | |
glDeleteShader(fragmentShader) | |
// bind shader data | |
glGenVertexArrays(1, &vao) | |
glGenBuffers(1, &vbo) | |
defer { glDeleteVertexArrays(1, &vao) } | |
defer { glDeleteBuffers(1, &vbo) } | |
let glfloatStride = MemoryLayout<GLfloat>.stride | |
glBindVertexArray(vao) | |
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vbo) | |
glBufferData(GLenum(GL_ARRAY_BUFFER), glfloatStride * vertices.count, vertices, GLenum(GL_STATIC_DRAW)) | |
// postion data | |
glVertexAttribPointer( | |
0, | |
GLint(verticesStride), | |
GLenum(GL_FLOAT), | |
GLboolean(GL_FALSE), | |
GLsizei(glfloatStride * dataStride), | |
nil | |
) | |
// color data | |
glVertexAttribPointer( | |
1, | |
GLint(colorStride), | |
GLenum(GL_FLOAT), | |
GLboolean(GL_FALSE), | |
GLsizei(glfloatStride * dataStride), | |
UnsafeRawPointer(bitPattern: glfloatStride * verticesStride) | |
) | |
glEnableVertexAttribArray(0) | |
glEnableVertexAttribArray(1) | |
glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0) | |
glBindVertexArray(0) | |
// clear | |
glClearColor(0, 0, 0.0, 1) | |
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) | |
// draw | |
glUseProgram(shaderProgram) | |
glBindVertexArray(vao) | |
glDrawArrays(GLenum(GL_TRIANGLES), 0, GLsizei(vertices.count/2)) | |
glBindVertexArray(0) | |
CGLFlushDrawable(ctx) | |
} | |
func checkShaderError(_ shader: GLuint) { | |
var success:GLint = 0 | |
var infoLog = [GLchar](repeating: 0, count: 512) | |
glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &success) | |
guard success == GL_TRUE else | |
{ | |
glGetShaderInfoLog(shader, 512, nil, &infoLog) | |
fatalError(String(cString:infoLog)) | |
} | |
} | |
func checkProgramError(_ program: GLuint) { | |
var success:GLint = 0 | |
var infoLog = [GLchar](repeating: 0, count: 512) | |
glGetProgramiv(program, GLenum(GL_LINK_STATUS), &success) | |
guard success == GL_TRUE else | |
{ | |
glGetProgramInfoLog(program, 512, nil, &infoLog) | |
fatalError(String(cString:infoLog)) | |
} | |
} | |
override func copyCGLPixelFormat(forDisplayMask mask: UInt32) -> CGLPixelFormatObj { | |
print("--------- Layer with EDR: \(wantsExtendedDynamicRangeContent) ---------") | |
print("--------- View with EDR: \(view.wantsExtendedDynamicRangeOpenGLSurface) ---------") | |
let (cglPixelFormat, bufferDepth) = Layer.createPixelFormat() | |
return cglPixelFormat | |
} | |
override func copyCGLContext(forPixelFormat pf: CGLPixelFormatObj) -> CGLContextObj { | |
let ctx = Layer.createContext(pf) | |
return ctx | |
} | |
class func createPixelFormat() -> (CGLPixelFormatObj, GLint) { | |
var pix: CGLPixelFormatObj? | |
var depth: GLint = 8 | |
var err: CGLError = CGLError(rawValue: 0) | |
let swRender = 0 | |
(pix, depth, err) = Layer.findPixelFormat() | |
guard let pixelFormat = pix, err == kCGLNoError else { | |
print("Couldn't create any CGL pixel format") | |
exit(1) | |
} | |
return (pixelFormat, depth) | |
} | |
class func findPixelFormat(_ software: Bool = false) -> (CGLPixelFormatObj?, GLint, CGLError) { | |
var pix: CGLPixelFormatObj? | |
var err: CGLError = CGLError(rawValue: 0) | |
var npix: GLint = 0 | |
for ver in glVersions { | |
var glBase = software ? glFormatSoftwareBase : glFormatBase | |
glBase.insert(CGLPixelFormatAttribute(ver.rawValue), at: 1) | |
var glFormat = [glBase] | |
glFormat += [glFormat10Bit] | |
glFormat += glFormatOptional | |
for index in stride(from: glFormat.count-1, through: 0, by: -1) { | |
let format = glFormat.flatMap { $0 } + [_CGLPixelFormatAttribute(rawValue: 0)] | |
err = CGLChoosePixelFormat(format, &pix, &npix) | |
if err == kCGLBadAttribute || err == kCGLBadPixelFormat || pix == nil { | |
glFormat.remove(at: index) | |
} else { | |
let attArray = format.map({ (value: _CGLPixelFormatAttribute) -> String in | |
return attributeLookUp[value.rawValue] ?? String(value.rawValue) | |
}) | |
print("Created CGL pixel format with attributes: " + | |
"\(attArray.joined(separator: ", "))") | |
return (pix, glFormat.contains(glFormat10Bit) ? 16 : 8, err) | |
} | |
} | |
} | |
let errS = String(cString: CGLErrorString(err)) | |
print("Couldn't create a " + | |
"\(software ? "software" : "hardware accelerated") " + | |
"CGL pixel format: \(errS) (\(err.rawValue))") | |
return (pix, 8, err) | |
} | |
class func createContext(_ pixelFormat: CGLPixelFormatObj) -> CGLContextObj { | |
var context: CGLContextObj? | |
let error = CGLCreateContext(pixelFormat, nil, &context) | |
guard let cglContext = context, error == kCGLNoError else { | |
let errS = String(cString: CGLErrorString(error)) | |
print("Couldn't create a CGLContext: " + errS) | |
exit(1) | |
} | |
return cglContext | |
} | |
} | |
class View: NSView { | |
init(frame frameRect: NSRect, _ edr: Bool = false) { | |
super.init(frame:frameRect) | |
autoresizingMask = [.width, .height] | |
wantsBestResolutionOpenGLSurface = true | |
wantsExtendedDynamicRangeOpenGLSurface = edr | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
} | |
class Window: NSWindow, NSWindowDelegate { | |
override var canBecomeKey: Bool { return true } | |
override var canBecomeMain: Bool { return true } | |
override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, | |
backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) { | |
super.init(contentRect:contentRect, styleMask:style, backing:backingStoreType, defer:flag) | |
minSize = NSMakeSize(200, 200) | |
makeMain() | |
makeKeyAndOrderFront(nil) | |
delegate = self | |
contentAspectRatio = contentView!.frame.size | |
} | |
} | |
class App: NSApplication, NSApplicationDelegate { | |
var window: Window? | |
var window2: Window? | |
func applicationDidFinishLaunching(_ notification: Notification) { | |
print("----------------------------- BEFORE EDR REQUEST ------------------------------") | |
printEDRScreenData() | |
print("-------------------------------------------------------------------------------") | |
NSApp.setActivationPolicy(.regular) | |
atexit_b { NSApp.setActivationPolicy(.prohibited) } | |
window = Window( | |
contentRect: NSMakeRect(0, 300, 1000, 500), | |
styleMask: [.titled, .closable, .miniaturizable, .resizable], | |
backing: .buffered, | |
defer: false | |
) | |
let view = View(frame: window?.contentView?.bounds ?? NSMakeRect(0, 300, 1000, 500)) | |
window?.contentView?.addSubview(view) | |
window?.title = "EDR deactivated" | |
view.layer = Layer(view) | |
view.wantsLayer = true | |
window2 = Window( | |
contentRect: NSMakeRect(1000, 300, 1000, 500), | |
styleMask: [.titled, .closable, .miniaturizable, .resizable], | |
backing: .buffered, | |
defer: false | |
) | |
let view2 = View(frame: window?.contentView?.bounds ?? NSMakeRect(1000, 300, 1000, 500), true) | |
window2?.contentView?.addSubview(view2) | |
window2?.title = "EDR activated" | |
view2.layer = Layer(view2, true) | |
view2.wantsLayer = true | |
NSApp.activate(ignoringOtherApps: true) | |
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) { | |
print("------------------------------ AFTER EDR REQUEST ------------------------------") | |
self.printEDRScreenData() | |
print("-------------------------------------------------------------------------------") | |
} | |
} | |
func printEDRScreenData() { | |
for screen in NSScreen.screens { | |
var maxPot: CGFloat = -1.0 | |
var maxRef: CGFloat = -1.0 | |
var maxRange: CGFloat = -1.0 | |
print(screen.displayName ?? "unknown") | |
if #available(macOS 10.15, *) { | |
maxPot = screen.maximumPotentialExtendedDynamicRangeColorComponentValue | |
maxRef = screen.maximumReferenceExtendedDynamicRangeColorComponentValue | |
} | |
maxRange = screen.maximumExtendedDynamicRangeColorComponentValue | |
print("maximumPotentialExtendedDynamicRangeColorComponentValue: \(maxPot)") | |
print("maximumReferenceExtendedDynamicRangeColorComponentValue: \(maxRef)") | |
print("maximumExtendedDynamicRangeColorComponentValue: \(maxRange)") | |
} | |
} | |
} | |
let app = App.shared | |
NSApp = app | |
app.delegate = app as? NSApplicationDelegate | |
app.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment