mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	
		
			
	
	
		
			272 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
		
		
			
		
	
	
			272 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
|   | /**************************************************************************/ | ||
|  | /*  embedded_gl_manager.mm                                                */ | ||
|  | /**************************************************************************/ | ||
|  | /*                         This file is part of:                          */ | ||
|  | /*                             GODOT ENGINE                               */ | ||
|  | /*                        https://godotengine.org                         */ | ||
|  | /**************************************************************************/ | ||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||
|  | /*                                                                        */ | ||
|  | /* Permission is hereby granted, free of charge, to any person obtaining  */ | ||
|  | /* a copy of this software and associated documentation files (the        */ | ||
|  | /* "Software"), to deal in the Software without restriction, including    */ | ||
|  | /* without limitation the rights to use, copy, modify, merge, publish,    */ | ||
|  | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | ||
|  | /* permit persons to whom the Software is furnished to do so, subject to  */ | ||
|  | /* the following conditions:                                              */ | ||
|  | /*                                                                        */ | ||
|  | /* The above copyright notice and this permission notice shall be         */ | ||
|  | /* included in all copies or substantial portions of the Software.        */ | ||
|  | /*                                                                        */ | ||
|  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | ||
|  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | ||
|  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | ||
|  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | ||
|  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | ||
|  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | ||
|  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | ||
|  | /**************************************************************************/ | ||
|  | 
 | ||
|  | #import "embedded_gl_manager.h" | ||
|  | 
 | ||
|  | #import "drivers/gles3/storage/texture_storage.h" | ||
|  | #import "platform_gl.h" | ||
|  | 
 | ||
|  | #if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) | ||
|  | 
 | ||
|  | #import <QuartzCore/QuartzCore.h> | ||
|  | #include <dlfcn.h> | ||
|  | 
 | ||
|  | GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14. | ||
|  | 
 | ||
|  | Error GLManagerEmbedded::create_context(GLWindow &p_win) { | ||
|  | 	NSOpenGLPixelFormatAttribute attributes[] = { | ||
|  | 		NSOpenGLPFADoubleBuffer, | ||
|  | 		NSOpenGLPFAClosestPolicy, | ||
|  | 		NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, | ||
|  | 		NSOpenGLPFAColorSize, 32, | ||
|  | 		NSOpenGLPFADepthSize, 24, | ||
|  | 		NSOpenGLPFAStencilSize, 8, | ||
|  | 		0 | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; | ||
|  | 	ERR_FAIL_NULL_V(pixel_format, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	p_win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context]; | ||
|  | 	ERR_FAIL_NULL_V(p_win.context, ERR_CANT_CREATE); | ||
|  | 	if (shared_context == nullptr) { | ||
|  | 		shared_context = p_win.context; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	[p_win.context makeCurrentContext]; | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | Error GLManagerEmbedded::window_create(DisplayServer::WindowID p_window_id, CALayer *p_layer, int p_width, int p_height) { | ||
|  | 	GLWindow win; | ||
|  | 	win.layer = p_layer; | ||
|  | 	win.width = 0; | ||
|  | 	win.height = 0; | ||
|  | 
 | ||
|  | 	if (create_context(win) != OK) { | ||
|  | 		return FAILED; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	windows[p_window_id] = win; | ||
|  | 	window_make_current(p_window_id); | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { | ||
|  | 	GLWindowElement *el = windows.find(p_window_id); | ||
|  | 	ERR_FAIL_NULL_MSG(el, "Window resize failed: window does not exist."); | ||
|  | 
 | ||
|  | 	GLWindow &win = el->get(); | ||
|  | 
 | ||
|  | 	if (win.width == (uint32_t)p_width && win.height == (uint32_t)p_height) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	win.width = (uint32_t)p_width; | ||
|  | 	win.height = (uint32_t)p_height; | ||
|  | 
 | ||
|  | 	win.destroy_framebuffers(); | ||
|  | 
 | ||
|  | 	for (FrameBuffer &fb : win.framebuffers) { | ||
|  | 		NSDictionary *surfaceProps = @{ | ||
|  | 			(NSString *)kIOSurfaceWidth : @(p_width), | ||
|  | 			(NSString *)kIOSurfaceHeight : @(p_height), | ||
|  | 			(NSString *)kIOSurfaceBytesPerElement : @(4), | ||
|  | 			(NSString *)kIOSurfacePixelFormat : @(kCVPixelFormatType_32BGRA), | ||
|  | 		}; | ||
|  | 		fb.surface = IOSurfaceCreate((__bridge CFDictionaryRef)surfaceProps); | ||
|  | 		if (fb.surface == nullptr) { | ||
|  | 			ERR_PRINT(vformat("Failed to create IOSurface: width=%d, height=%d", p_width, p_height)); | ||
|  | 			win.destroy_framebuffers(); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		glGenTextures(1, &fb.tex); | ||
|  | 		glBindTexture(GL_TEXTURE_RECTANGLE, fb.tex); | ||
|  | 
 | ||
|  | 		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
|  | 		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
|  | 		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
|  | 		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
|  | 
 | ||
|  | 		CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(), | ||
|  | 				GL_TEXTURE_RECTANGLE, | ||
|  | 				GL_RGBA, | ||
|  | 				p_width, | ||
|  | 				p_height, | ||
|  | 				GL_BGRA, | ||
|  | 				GL_UNSIGNED_INT_8_8_8_8_REV, | ||
|  | 				fb.surface, | ||
|  | 				0); | ||
|  | 		if (err != kCGLNoError) { | ||
|  | 			String err_string = String(CGLErrorString(err)); | ||
|  | 			ERR_PRINT(vformat("CGLTexImageIOSurface2D failed (%d): %s", err, err_string)); | ||
|  | 			win.destroy_framebuffers(); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		glGenFramebuffers(1, &fb.fbo); | ||
|  | 		glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); | ||
|  | 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, fb.tex, 0); | ||
|  | 
 | ||
|  | 		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||
|  | 			ERR_PRINT("Unable to create framebuffer from IOSurface texture."); | ||
|  | 			win.destroy_framebuffers(); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		glBindFramebuffer(GL_FRAMEBUFFER, 0); | ||
|  | 		glBindTexture(GL_TEXTURE_RECTANGLE, 0); | ||
|  | 	} | ||
|  | 	win.current_fb = 0; | ||
|  | 	GLES3::TextureStorage::system_fbo = win.framebuffers[win.current_fb].fbo; | ||
|  | 	win.is_valid = true; | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::GLWindow::destroy_framebuffers() { | ||
|  | 	is_valid = false; | ||
|  | 	GLES3::TextureStorage::system_fbo = 0; | ||
|  | 
 | ||
|  | 	for (FrameBuffer &fb : framebuffers) { | ||
|  | 		if (fb.fbo) { | ||
|  | 			glDeleteFramebuffers(1, &fb.fbo); | ||
|  | 			fb.fbo = 0; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (fb.tex) { | ||
|  | 			glDeleteTextures(1, &fb.tex); | ||
|  | 			fb.tex = 0; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (fb.surface) { | ||
|  | 			IOSurfaceRef old_surface = fb.surface; | ||
|  | 			fb.surface = nullptr; | ||
|  | 			CFRelease(old_surface); | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | Size2i GLManagerEmbedded::window_get_size(DisplayServer::WindowID p_window_id) const { | ||
|  | 	const GLWindowElement *el = windows.find(p_window_id); | ||
|  | 	if (el == nullptr) { | ||
|  | 		return Size2i(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const GLWindow &win = el->value(); | ||
|  | 	return Size2i(win.width, win.height); | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::window_destroy(DisplayServer::WindowID p_window_id) { | ||
|  | 	GLWindowElement *el = windows.find(p_window_id); | ||
|  | 	if (el == nullptr) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (current_window == p_window_id) { | ||
|  | 		current_window = DisplayServer::INVALID_WINDOW_ID; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	windows.erase(el); | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::release_current() { | ||
|  | 	if (current_window == DisplayServer::INVALID_WINDOW_ID) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	[NSOpenGLContext clearCurrentContext]; | ||
|  | 	current_window = DisplayServer::INVALID_WINDOW_ID; | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::window_make_current(DisplayServer::WindowID p_window_id) { | ||
|  | 	if (current_window == p_window_id) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const GLWindowElement *el = windows.find(p_window_id); | ||
|  | 	if (el == nullptr) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const GLWindow &win = el->value(); | ||
|  | 	[win.context makeCurrentContext]; | ||
|  | 
 | ||
|  | 	current_window = p_window_id; | ||
|  | } | ||
|  | 
 | ||
|  | void GLManagerEmbedded::swap_buffers() { | ||
|  | 	GLWindow &win = windows[current_window]; | ||
|  | 	[win.context flushBuffer]; | ||
|  | 
 | ||
|  | 	static bool last_valid = false; | ||
|  | 	if (!win.is_valid) { | ||
|  | 		if (last_valid) { | ||
|  | 			ERR_PRINT("GLWindow framebuffers are invalid."); | ||
|  | 			last_valid = false; | ||
|  | 		} | ||
|  | 		return; | ||
|  | 	} | ||
|  | 	last_valid = true; | ||
|  | 
 | ||
|  | 	[CATransaction begin]; | ||
|  | 	[CATransaction setDisableActions:YES]; | ||
|  | 	win.layer.contents = (__bridge id)win.framebuffers[win.current_fb].surface; | ||
|  | 	[CATransaction commit]; | ||
|  | 	win.current_fb = (win.current_fb + 1) % BUFFER_COUNT; | ||
|  | 	GLES3::TextureStorage::system_fbo = win.framebuffers[win.current_fb].fbo; | ||
|  | } | ||
|  | 
 | ||
|  | Error GLManagerEmbedded::initialize() { | ||
|  | 	return framework_loaded ? OK : ERR_CANT_CREATE; | ||
|  | } | ||
|  | 
 | ||
|  | GLManagerEmbedded::GLManagerEmbedded() { | ||
|  | 	NSBundle *framework = [NSBundle bundleWithIdentifier:@"com.apple.opengl"]; | ||
|  | 	if ([framework load]) { | ||
|  | 		void *library_handle = dlopen([framework.executablePath UTF8String], RTLD_NOW); | ||
|  | 		if (library_handle) { | ||
|  | 			CGLGetCurrentContext = (CGLGetCurrentContextPtr)dlsym(library_handle, "CGLGetCurrentContext"); | ||
|  | 			CGLTexImageIOSurface2D = (CGLTexImageIOSurface2DPtr)dlsym(library_handle, "CGLTexImageIOSurface2D"); | ||
|  | 			CGLErrorString = (CGLErrorStringPtr)dlsym(library_handle, "CGLErrorString"); | ||
|  | 			framework_loaded = CGLGetCurrentContext && CGLTexImageIOSurface2D && CGLErrorString; | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | GLManagerEmbedded::~GLManagerEmbedded() { | ||
|  | 	release_current(); | ||
|  | } | ||
|  | 
 | ||
|  | GODOT_CLANG_WARNING_POP | ||
|  | 
 | ||
|  | #endif // MACOS_ENABLED && GLES3_ENABLED |