Parcourir la source

added osx cocoa support files

Palana il y a 12 ans
Parent
commit
d7a04aea8c
5 fichiers modifiés avec 611 ajouts et 1 suppressions
  1. 253 0
      libobs-opengl/gl-cocoa.m
  2. 4 1
      libobs/graphics/graphics.h
  3. 61 0
      libobs/obs-cocoa.c
  4. 100 0
      libobs/util/platform-cocoa.c
  5. 193 0
      test/osx/test.mm

+ 253 - 0
libobs-opengl/gl-cocoa.m

@@ -0,0 +1,253 @@
+/******************************************************************************
+    Copyright (C) 2013 by Ruwen Hahn <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <GL/glew.h>
+#include "gl-subsystem.h"
+#include <OpenGL/OpenGL.h>
+
+#import <Cocoa/Cocoa.h>
+#import <AppKit/AppKit.h>
+
+
+//#include "util/darray.h"
+
+
+struct gl_windowinfo {
+	NSView *view;
+};
+
+struct gl_platform {
+	NSOpenGLContext *context;
+	GLuint vao;
+	struct gs_swap_chain swap;
+};
+
+static NSOpenGLContext *gl_context_create(struct gs_init_data *info)
+{
+	unsigned attrib_count = 0;
+
+#define ADD_ATTR(x) { attributes[attrib_count++] = (NSOpenGLPixelFormatAttribute)x; }
+#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); }
+
+	NSOpenGLPixelFormatAttribute attributes[40];
+
+	switch(info->num_backbuffers) {
+		case 0:
+			break;
+		case 1:
+			ADD_ATTR(NSOpenGLPFADoubleBuffer);
+			break;
+		case 2:
+			ADD_ATTR(NSOpenGLPFATripleBuffer);
+			break;
+		default:
+			blog(LOG_ERROR, "Requested backbuffers (%d) not supported", info->num_backbuffers);
+	}
+
+	ADD_ATTR(NSOpenGLPFAClosestPolicy);
+	ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
+
+	int color_bits = 0;//get_color_format_bits(info->format);
+	if(color_bits == 0) color_bits = 24;
+	else if(color_bits < 15) color_bits = 15;
+
+	ADD_ATTR2(NSOpenGLPFAColorSize, color_bits);
+
+	ADD_ATTR2(NSOpenGLPFAAlphaSize, 8);
+
+	ADD_ATTR2(NSOpenGLPFADepthSize, 16);
+
+	ADD_ATTR(0);
+
+#undef ADD_ATTR2
+#undef ADD_ATTR
+
+	NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];;
+	if(!pf) {
+		blog(LOG_ERROR, "Failed to create pixel format");
+		return NULL;
+	}
+
+	NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
+	[pf release];
+	if(!context) {
+		blog(LOG_ERROR, "Failed to create context");
+		return NULL;
+	}
+
+	[context setView:info->view];
+
+	return context;
+}
+
+static inline void required_extension_error(const char *extension)
+{
+	blog(LOG_ERROR, "OpenGL extension %s is required", extension);
+}
+
+static bool gl_init_extensions(device_t device)
+{
+	glewExperimental=true;
+	GLenum error = glewInit();
+	if(error != GLEW_OK) {
+	       blog(LOG_ERROR, "glewInit failed, %u\n%s\n", error, glewGetErrorString(error));
+	       return false;
+	}
+
+	if(!GLEW_VERSION_2_1) {
+	       blog(LOG_ERROR, "OpenGL 2.1 minimum required by the graphics "
+	                       "adapter");
+	       return false;
+	}
+
+	if(!GLEW_ARB_framebuffer_object) {
+		required_extension_error("GL_ARB_framebuffer_object");
+		return false;
+	}
+
+	if(!GLEW_ARB_separate_shader_objects) {
+		required_extension_error("GL_ARB_separate_shader_objects");
+		return false;
+	}
+
+	glGetError(); //something inside glew produces error code 1280 (invalid enum)
+
+	device->copy_type = COPY_TYPE_FBO_BLIT;
+
+	return true;
+}
+
+static bool gl_init_default_swap(struct gl_platform *plat, device_t dev,
+		struct gs_init_data *info)
+{
+	if(!(plat->context = gl_context_create(info)))
+		return false;
+
+	plat->swap.device = dev;
+	plat->swap.info	  = *info;
+	plat->swap.wi     = gl_windowinfo_create(info);
+
+	return plat->swap.wi != NULL;
+}
+
+struct gl_platform *gl_platform_create(device_t device,
+		struct gs_init_data *info)
+{
+	struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
+	memset(plat, 0, sizeof(struct gl_platform));
+
+	if(!gl_init_default_swap(plat, device, info))
+		goto fail;
+
+	[plat->context makeCurrentContext];
+
+	if(!gl_init_extensions(device))
+		goto fail;
+
+	gl_gen_vertex_arrays(1, &plat->vao);
+	gl_bind_vertex_array(plat->vao);
+
+	if (GLEW_ARB_seamless_cube_map) {
+		glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+		gl_success("GL_TEXTURE_CUBE_MAP_SEAMLESS");
+	}
+
+	return plat;
+
+fail:
+	blog(LOG_ERROR, "gl_platform_create failed");
+	gl_platform_destroy(plat);
+	return NULL;
+}
+
+struct gs_swap_chain *gl_platform_getswap(struct gl_platform *platform)
+{
+	if(platform)
+		return &platform->swap;
+	return NULL;
+}
+
+void gl_platform_destroy(struct gl_platform *platform)
+{
+	if(!platform)
+		return;
+
+	gl_delete_vertex_arrays(1, &platform->vao);
+	[platform->context release];
+	platform->context = nil;
+	gl_windowinfo_destroy(platform->swap.wi);
+
+	bfree(platform);
+}
+
+struct gl_windowinfo *gl_windowinfo_create(struct gs_init_data *info)
+{
+	if(!info)
+		return NULL;
+
+	if(!info->view)
+		return NULL;
+
+	struct gl_windowinfo *wi = bmalloc(sizeof(struct gl_windowinfo));
+	memset(wi, 0, sizeof(struct gl_windowinfo));
+
+	wi->view = info->view;
+
+	return wi;
+}
+
+void gl_windowinfo_destroy(struct gl_windowinfo *wi)
+{
+	if(!wi)
+		return;
+
+	wi->view = nil;
+	bfree(wi);
+}
+
+void device_entercontext(device_t device)
+{
+	[device->plat->context makeCurrentContext];
+	//gl_bind_vertex_array(device->plat->vao);
+}
+
+void device_leavecontext(device_t device)
+{
+	[NSOpenGLContext clearCurrentContext];
+}
+
+void device_load_swapchain(device_t device, swapchain_t swap)
+{
+	if(!swap)
+		swap = &device->plat->swap;
+
+	if(device->cur_swap == swap)
+		return;
+
+	device->cur_swap = swap;
+}
+
+void device_present(device_t device)
+{
+	[device->plat->context flushBuffer];
+}
+
+void gl_getclientsize(struct gs_swap_chain *swap, uint32_t *width, uint32_t *height)
+{
+	if(width) *width = swap->info.cx;
+	if(height) *height = swap->info.cy;
+}

+ 4 - 1
libobs/graphics/graphics.h

@@ -19,6 +19,9 @@
 
 
 #include "../util/bmem.h"
 #include "../util/bmem.h"
 #include "input.h"
 #include "input.h"
+#ifdef __APPLE__
+#include <objc/objc-runtime.h>
+#endif
 
 
 /*
 /*
  * This is an API-independent graphics subsystem wrapper.
  * This is an API-independent graphics subsystem wrapper.
@@ -410,7 +413,7 @@ struct gs_init_data {
 #if defined(_WIN32)
 #if defined(_WIN32)
 	void                    *hwnd;
 	void                    *hwnd;
 #elif defined(__APPLE__)
 #elif defined(__APPLE__)
-	/* TODO */
+	__unsafe_unretained id  view;
 #elif defined(__posix__)
 #elif defined(__posix__)
 	/* TODO */
 	/* TODO */
 #endif
 #endif

+ 61 - 0
libobs/obs-cocoa.c

@@ -0,0 +1,61 @@
+/******************************************************************************
+    Copyright (C) 2013 by Ruwen Hahn <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "util/platform.h"
+#include "util/dstr.h"
+#include "obs.h"
+#include "obs-data.h"
+
+#include <unistd.h>
+
+// support both foo.so and libfoo.so for now
+static const char *plugin_patterns[] = {
+	"../plugins/%s.so",
+	"../plugins/lib%s.so"
+};
+static const int plugin_patterns_size = sizeof(plugin_patterns)/sizeof(plugin_patterns[0]);
+
+char *find_plugin(const char *plugin)
+{
+	struct dstr path;
+	dstr_init(&path);
+	for(int i = 0; i < plugin_patterns_size; i++) {
+		dstr_printf(&path, plugin_patterns[i], plugin);
+		if(!access(path.array, F_OK))
+			break;
+	}
+
+	return path.array;
+}
+
+/* on windows, points to [base directory]/libobs */
+char *find_libobs_data_file(const char *file)
+{
+	struct dstr path;
+	dstr_init_copy(&path, "../libobs/");
+	dstr_cat(&path, file);
+	return path.array;
+}
+
+/* on windows, data files should always be in [base directory]/data */
+char *obs_find_plugin_file(const char *file)
+{
+	struct dstr path;
+	dstr_init_copy(&path, "../data/");
+	dstr_cat(&path, file);
+	return path.array;
+}

+ 100 - 0
libobs/util/platform-cocoa.c

@@ -0,0 +1,100 @@
+/******************************************************************************
+  Copyright (c) 2013 by Ruwen Hahn <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include "base.h"
+#include "platform.h"
+#include "dstr.h"
+
+#include <dlfcn.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+
+void *os_dlopen(const char *path)
+{
+	struct dstr dylib_name;
+	dstr_init_copy(&dylib_name, path);
+	if(!dstr_find(&dylib_name, ".so"))
+		dstr_cat(&dylib_name, ".so");
+
+	void *res = dlopen(dylib_name.array, RTLD_LAZY);
+	if(!res)
+		blog(LOG_ERROR, "os_dlopen(%s->%s): %s\n",
+				path, dylib_name.array, dlerror());
+
+	dstr_free(&dylib_name);
+	return res;
+}
+
+void *os_dlsym(void *module, const char *func)
+{
+	return dlsym(module, func);
+}
+
+void os_dlclose(void *module)
+{
+	dlclose(module);
+}
+
+void os_sleepto_ns(uint64_t time_target)
+{
+	uint64_t current = os_gettime_ns();
+	if(time_target < current)
+		return;
+	time_target -= current;
+	struct timespec req,
+			remain;
+	memset(&req, 0, sizeof(req));
+	memset(&remain, 0, sizeof(remain));
+	req.tv_sec = time_target/1000000000;
+	req.tv_nsec = time_target%1000000000;
+	while(nanosleep(&req, &remain))
+	{
+		req = remain;
+		memset(&remain, 0, sizeof(remain));
+	}
+}
+
+void os_sleep_ms(uint32_t duration)
+{
+	usleep(duration*1000);
+}
+
+uint64_t os_gettime_ns(void)
+{
+	uint64_t t = mach_absolute_time();
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"	
+	Nanoseconds nano = AbsoluteToNanoseconds(*(AbsoluteTime*) &t);
+#pragma clang diagnostic pop
+	return *(uint64_t*) &nano;
+}
+
+uint64_t os_gettime_ms(void)
+{
+	return  os_gettime_ns()/1000000;
+}
+

+ 193 - 0
test/osx/test.mm

@@ -0,0 +1,193 @@
+#include <stdio.h>
+#include <time.h>
+
+#include <functional>
+#include <memory>
+
+#import <Cocoa/Cocoa.h>
+#import <AppKit/AppKit.h>
+#import <OpenGL/OpenGL.h>
+
+#include "util/base.h"
+#include "obs.h"
+
+static const int cx = 800;
+static const int cy = 600;
+
+
+
+/* --------------------------------------------------- */
+
+using SourceContext = std::unique_ptr<obs_source,
+      std::function<void(obs_source_t)>>;
+static SourceContext autorelease(obs_source_t s)
+{
+	return SourceContext(s, obs_source_destroy);
+}
+
+using SceneContext = std::unique_ptr<obs_scene,
+      std::function<void(obs_scene_t)>>;
+static SceneContext autorelease(obs_scene_t s)
+{
+	return SceneContext(s, obs_scene_destroy);
+}
+
+/* --------------------------------------------------- */
+
+static void CreateOBS(NSWindow *win)
+{
+	struct video_info vi;
+	memset(&vi, 0, sizeof(struct video_info));
+	vi.fps_num = 30000;
+	vi.fps_den = 1001;
+	vi.width   = cx;
+	vi.height  = cy;
+	vi.name    = "video";
+
+	struct gs_init_data gsid;
+	memset(&gsid, 0, sizeof(gsid));
+	gsid.view            = [win contentView];
+	gsid.cx              = cx;
+	gsid.cy              = cy;
+	gsid.num_backbuffers = 2;
+	gsid.format          = GS_RGBA;
+
+	if (!obs_startup("libobs-opengl", &gsid, &vi, NULL))
+		throw "Couldn't create OBS";
+}
+
+static void AddTestItems(obs_scene_t scene, obs_source_t source)
+{
+	obs_sceneitem_t item = NULL;
+	struct vec2 v2;
+
+	item = obs_scene_add(scene, source);
+	vec2_set(&v2, 100.0f, 200.0f);
+	obs_sceneitem_setpos(item, &v2);
+	obs_sceneitem_setrot(item, 10.0f);
+	vec2_set(&v2, 20.0f, 2.0f);
+	obs_sceneitem_setscale(item, &v2);
+
+	item = obs_scene_add(scene, source);
+	vec2_set(&v2, 200.0f, 100.0f);
+	obs_sceneitem_setpos(item, &v2);
+	obs_sceneitem_setrot(item, -45.0f);
+	vec2_set(&v2, 5.0f, 7.0f);
+	obs_sceneitem_setscale(item, &v2);
+}
+
+@interface window_closer : NSObject {}
+@end
+
+@implementation window_closer
+
++(id)window_closer
+{
+	return [[window_closer alloc] init];
+}
+
+-(void)windowWillClose:(NSNotification *)notification
+{
+	[NSApp stop:self];
+}
+@end
+
+static NSWindow *CreateTestWindow()
+{
+	[NSApplication sharedApplication];
+
+	ProcessSerialNumber psn = {0, kCurrentProcess};
+	TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"	
+	SetFrontProcess(&psn);
+#pragma clang diagnostic pop
+
+	NSRect content_rect = NSMakeRect(0, 0, cx, cy);
+	NSWindow *win = [[NSWindow alloc]
+		initWithContentRect:content_rect
+		styleMask:NSTitledWindowMask | NSClosableWindowMask
+		backing:NSBackingStoreBuffered
+		defer:NO];
+	if(!win)
+		return nil;
+
+	[win orderFrontRegardless];
+
+	NSView *view = [[NSView alloc] initWithFrame:content_rect];
+	if(!view)
+		return nil;
+
+
+	[win setContentView:view];
+	[win setTitle:@"foo"];
+	[win center];
+	[win makeMainWindow];
+	[win setDelegate:[window_closer window_closer]];
+	return win;
+}
+
+static void test()
+{
+	try {
+		NSWindow *win = CreateTestWindow();
+		if (!win)
+			throw "Couldn't create main window";
+
+		CreateOBS(win);
+
+		/* ------------------------------------------------------ */
+		/* load module */
+		if (obs_load_module("test-input") != 0)
+			throw "Couldn't load module";
+
+		/* ------------------------------------------------------ */
+		/* create source */
+		SourceContext source = autorelease(obs_source_create(SOURCE_INPUT,
+				"random", NULL));
+		if (!source)
+			throw "Couldn't create random test source";
+
+		/* ------------------------------------------------------ */
+		/* create filter */
+		SourceContext filter = autorelease(obs_source_create(SOURCE_FILTER,
+				"test", NULL));
+		if (!filter)
+			throw "Couldn't create test filter";
+		obs_source_filter_add(source.get(), filter.get());
+
+		/* ------------------------------------------------------ */
+		/* create scene and add source to scene (twice) */
+		SceneContext scene = autorelease(obs_scene_create());
+		if (!scene)
+			throw "Couldn't create scene";
+
+		AddTestItems(scene.get(), source.get());
+
+		/* ------------------------------------------------------ */
+		/* set the scene as the primary draw source and go */
+		obs_set_primary_source(obs_scene_getsource(scene.get()));
+
+		[NSApp run];
+
+		obs_set_primary_source(NULL);
+
+	} catch (char const *error) {
+		printf("%s\n", error);
+	}
+
+	obs_shutdown();
+
+	blog(LOG_INFO, "Number of memory leaks: %zu", bnum_allocs());
+}
+
+/* --------------------------------------------------- */
+
+int main()
+{
+	@autoreleasepool {
+		test();
+	}
+
+	return 0;
+}