瀏覽代碼

Add `thread.Pool` with example in demo.odin; Update linalg to support handness changes for projection matrices

gingerBill 5 年之前
父節點
當前提交
3bd00fd6b7

+ 23 - 8
core/math/linalg/specific.odin

@@ -737,35 +737,50 @@ matrix4_look_at :: proc(eye, centre, up: Vector3) -> Matrix4 {
 }
 
 
-matrix4_perspective :: proc(fovy, aspect, near, far: Float) -> (m: Matrix4) {
+matrix4_perspective :: proc(fovy, aspect, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
 	tan_half_fovy := math.tan(0.5 * fovy);
 	m[0][0] = 1 / (aspect*tan_half_fovy);
 	m[1][1] = 1 / (tan_half_fovy);
-	m[2][2] = -(far + near) / (far - near);
-	m[2][3] = -1;
+	m[2][2] = +(far + near) / (far - near);
+	m[2][3] = +1;
 	m[3][2] = -2*far*near / (far - near);
+
+	if flip_z_axis {
+		m[2] = -m[2];
+	}
+
 	return;
 }
 
 
-matrix_ortho3d :: proc(left, right, bottom, top, near, far: Float) -> (m: Matrix4) {
+matrix_ortho3d :: proc(left, right, bottom, top, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
 	m[0][0] = +2 / (right - left);
 	m[1][1] = +2 / (top - bottom);
-	m[2][2] = -2 / (far - near);
+	m[2][2] = +2 / (far - near);
 	m[3][0] = -(right + left)   / (right - left);
 	m[3][1] = -(top   + bottom) / (top - bottom);
 	m[3][2] = -(far + near) / (far- near);
 	m[3][3] = 1;
+
+	if flip_z_axis {
+		m[2] = -m[2];
+	}
+
 	return;
 }
 
 
-matrix4_infinite_perspective :: proc(fovy, aspect, near: Float) -> (m: Matrix4) {
+matrix4_infinite_perspective :: proc(fovy, aspect, near: Float, flip_z_axis := true) -> (m: Matrix4) {
 	tan_half_fovy := math.tan(0.5 * fovy);
 	m[0][0] = 1 / (aspect*tan_half_fovy);
 	m[1][1] = 1 / (tan_half_fovy);
-	m[2][2] = -1;
-	m[2][3] = -1;
+	m[2][2] = +1;
+	m[2][3] = +1;
 	m[3][2] = -2*near;
+
+	if flip_z_axis {
+		m[2] = -m[2];
+	}
+
 	return;
 }

+ 1 - 1
core/sync/sync_windows.odin

@@ -83,4 +83,4 @@ condition_signal :: proc(using c: ^Condition) {
 condition_wait_for :: proc(using c: ^Condition) {
 	result := win32.wait_for_single_object(event, win32.INFINITE);
 	assert(result != win32.WAIT_FAILED);
-}
+}

+ 3 - 1
core/sys/unix/pthread_unix.odin

@@ -51,6 +51,8 @@ foreign pthread {
 	// know what you are doing!
 	pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---;
 	pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---;
+
+	pthread_yield :: proc() -> c.int ---;
 }
 
 @(default_calling_convention="c")
@@ -104,4 +106,4 @@ foreign pthread {
 	pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int ---;
 	pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int ---;
 
-}
+}

+ 3 - 3
core/thread/thread.odin

@@ -1,6 +1,6 @@
-package thread;
+package thread
 
-import "core:runtime";
+import "core:runtime"
 
 Thread_Proc :: #type proc(^Thread);
 
@@ -12,4 +12,4 @@ Thread :: struct {
 
 	init_context:     runtime.Context,
 	use_init_context: bool,
-}
+}

+ 147 - 0
core/thread/thread_pool.odin

@@ -0,0 +1,147 @@
+package thread
+
+import "intrinsics"
+import "core:sync"
+import "core:mem"
+
+Task_Status :: enum i32 {
+	Ready,
+	Busy,
+	Waiting,
+	Term,
+}
+
+Task_Proc :: #type proc(task: ^Task);
+
+Task :: struct {
+	procedure: Task_Proc,
+	data: rawptr,
+	user_index: int,
+}
+
+Task_Id :: distinct i32;
+INVALID_TASK_ID :: Task_Id(-1);
+
+
+Pool :: struct {
+	allocator:             mem.Allocator,
+	mutex:                 sync.Mutex,
+	sem_available:         sync.Semaphore,
+	processing_task_count: int, // atomic
+	is_running:            bool,
+
+	threads: []^Thread,
+
+	tasks: [dynamic]Task,
+}
+
+pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
+	worker_thread_internal :: proc(t: ^Thread) {
+		pool := (^Pool)(t.data);
+
+		for pool.is_running {
+			sync.semaphore_wait_for(&pool.sem_available);
+
+			if task, ok := pool_try_and_pop_task(pool); ok {
+				pool_do_work(pool, &task);
+			}
+		}
+
+		sync.semaphore_post(&pool.sem_available, 1);
+	}
+
+
+	context.allocator = allocator;
+	pool.allocator = allocator;
+	pool.tasks = make([dynamic]Task);
+	pool.threads = make([]^Thread, thread_count);
+
+	sync.mutex_init(&pool.mutex);
+	sync.semaphore_init(&pool.sem_available);
+	pool.is_running = true;
+
+	for _, i in pool.threads {
+		t := create(worker_thread_internal);
+		t.user_index = i;
+		t.data = pool;
+		pool.threads[i] = t;
+	}
+}
+
+pool_destroy :: proc(pool: ^Pool) {
+	delete(pool.tasks);
+	delete(pool.threads, pool.allocator);
+
+	sync.mutex_destroy(&pool.mutex);
+	sync.semaphore_destroy(&pool.sem_available);
+}
+
+pool_start :: proc(pool: ^Pool) {
+	for t in pool.threads {
+		start(t);
+	}
+}
+
+pool_join :: proc(pool: ^Pool) {
+	pool.is_running = false;
+
+	sync.semaphore_post(&pool.sem_available, len(pool.threads));
+
+	yield();
+
+	for t in pool.threads {
+		join(t);
+	}
+}
+
+pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
+	sync.mutex_lock(&pool.mutex);
+	defer sync.mutex_unlock(&pool.mutex);
+
+	task: Task;
+	task.procedure = procedure;
+	task.data = data;
+	task.user_index = user_index;
+
+	append(&pool.tasks, task);
+	sync.semaphore_post(&pool.sem_available, 1);
+}
+
+pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
+	if sync.mutex_try_lock(&pool.mutex) {
+		if len(pool.tasks) != 0 {
+			intrinsics.atomic_add(&pool.processing_task_count, 1);
+			task = pool.tasks[0];
+			got_task = true;
+			ordered_remove(&pool.tasks, 0);
+		}
+		sync.mutex_unlock(&pool.mutex);
+	}
+	return;
+}
+
+
+pool_do_work :: proc(pool: ^Pool, task: ^Task) {
+	task.procedure(task);
+	intrinsics.atomic_sub(&pool.processing_task_count, 1);
+}
+
+
+pool_wait_and_process :: proc(pool: ^Pool) {
+	for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
+		if task, ok := pool_try_and_pop_task(pool); ok {
+			pool_do_work(pool, &task);
+		}
+
+		// Safety kick
+		if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
+			sync.mutex_lock(&pool.mutex);
+			sync.semaphore_post(&pool.sem_available, len(pool.tasks));
+			sync.mutex_unlock(&pool.mutex);
+		}
+
+		yield();
+	}
+
+	pool_join(pool);
+}

+ 5 - 0
core/thread/thread_unix.odin

@@ -155,3 +155,8 @@ destroy :: proc(t: ^Thread) {
 	t.unix_thread = {};
 	free(t);
 }
+
+
+yield :: proc() {
+	unix.pthread_yield()
+}

+ 4 - 0
core/thread/thread_windows.odin

@@ -92,3 +92,7 @@ destroy :: proc(thread: ^Thread) {
 terminate :: proc(using thread : ^Thread, exit_code : u32) {
 	win32.terminate_thread(win32_thread, exit_code);
 }
+
+yield :: proc() {
+	win32.sleep(0);
+}

+ 52 - 24
examples/demo/demo.odin

@@ -4,6 +4,7 @@ import "core:fmt"
 import "core:mem"
 import "core:os"
 import "core:thread"
+import "core:time"
 import "core:reflect"
 import "intrinsics"
 
@@ -1102,38 +1103,65 @@ prefix_table := [?]string{
 threading_example :: proc() {
 	fmt.println("\n# threading_example");
 
-	worker_proc :: proc(t: ^thread.Thread) {
-		for iteration in 1..5 {
-			fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
-			fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
-			// win32.sleep(1);
+	{ // Basic Threads
+		fmt.println("\n## Basic Threads");
+			worker_proc :: proc(t: ^thread.Thread) {
+			for iteration in 1..5 {
+				fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
+				fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
+				time.sleep(1 * time.Millisecond);
+			}
 		}
-	}
 
-	threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
-	defer delete(threads);
+		threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
+		defer delete(threads);
 
-	for in prefix_table {
-		if t := thread.create(worker_proc); t != nil {
-			t.init_context = context;
-			t.use_init_context = true;
-			t.user_index = len(threads);
-			append(&threads, t);
-			thread.start(t);
+		for in prefix_table {
+			if t := thread.create(worker_proc); t != nil {
+				t.init_context = context;
+				t.use_init_context = true;
+				t.user_index = len(threads);
+				append(&threads, t);
+				thread.start(t);
+			}
 		}
-	}
 
-	for len(threads) > 0 {
-		for i := 0; i < len(threads); /**/ {
-			if t := threads[i]; thread.is_done(t) {
-				fmt.printf("Thread %d is done\n", t.user_index);
-				thread.destroy(t);
+		for len(threads) > 0 {
+			for i := 0; i < len(threads); /**/ {
+				if t := threads[i]; thread.is_done(t) {
+					fmt.printf("Thread %d is done\n", t.user_index);
+					thread.destroy(t);
+
+					ordered_remove(&threads, i);
+				} else {
+					i += 1;
+				}
+			}
+		}
+	}
 
-				ordered_remove(&threads, i);
-			} else {
-				i += 1;
+	{ // Thread Pool
+		fmt.println("\n## Thread Pool");
+		task_proc :: proc(t: ^thread.Task) {
+			index := t.user_index % len(prefix_table);
+			for iteration in 1..5 {
+				fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration);
+				fmt.printf("`%s`: iteration %d\n", prefix_table[index], iteration);
+				time.sleep(1 * time.Millisecond);
 			}
 		}
+
+		pool: thread.Pool;
+		thread.pool_init(pool=&pool, thread_count=3);
+		defer thread.pool_destroy(&pool);
+
+
+		for i in 0..<30 {
+			thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i);
+		}
+
+		thread.pool_start(&pool);
+		thread.pool_wait_and_process(&pool);
 	}
 }