• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.

Craig_jb

Members
  • Content count

    45
  • Joined

  • Last visited

Community Reputation

181 Neutral

About Craig_jb

  • Rank
    Member
  1. I've scoured the internet for hours on this one and still cannot figure it out! Any help is appreciated. [CODE] typedef boost::function<void ()> TaskKernel; void TaskScheduler::pushTask(const TaskData& taskData, TaskKernel kernel) { Task* task = m_taskPool->obtain(); task->m_kernel = TaskKernel(kernel); task->m_taskData = taskData; } // later in the calling code sch->pushTask(TaskData(), boost::bind(&run); [/CODE] I get an EXC_BAD_ACCESS in boost move_assign function for the copy constructor or operator= every time. I tried passing the function without boost::bind and with all different variations. No luck... Is there a special technique I am missing for passing boost::functions around?
  2. The premake build system is by far my favorite. Maybe you will find it useful. Premake allows you to write build configurations in Lua, and then use simple commands to generate makefiles, Visual Studio projects, or other files for your project. I describe my solution and all its projects once, and then generate any build file I want. [url="http://industriousone.com/what-premake"]http://industriousone.com/what-premake[/url]
  3. I've changed the WorkerThread implementation to avoid the race conditions. It passes testing through Helgrind at least. [url="https://gist.github.com/2625036"]https://gist.github.com/2625036[/url] [source lang="cpp"] [color=#999988][i]/// This is not written to compile. I just copied everything into one file to make viewing easy[/i][/color] [color=#999988][i]/// Here is a sample usage (task definitions and implementations omitted)[/i][/color] [color=#445588][b]int[/b][/color] main() { TaskScheduler scheduler; MyTaskA taskA; MyTaskB taskB; BOOL running [b]=[/b] [b]true[/b] [b]while[/b] (running) { scheduler.pushTask([b]&[/b]taskA); scheduler.pushTask([b]&[/b]taskB); scheduler.runUntilDone(); } } [color=#999999][b]#include "boost/thread.hpp"[/b][/color] [b]class[/b] [color=#445588][b]Task[/b][/color]; [b]class[/b] [color=#445588][b]TaskScheduler[/b][/color]; [b]class[/b] [color=#445588][b]WorkerThread[/b][/color]; [color=#999988][i]//// HEADERS ////[/i][/color] [color=#999988][i]/// Abstract interface for all tasks run by scheduler[/i][/color] [b]class[/b] [color=#445588][b]Task[/b][/color] { [b]public[/b][b]:[/b] [b]virtual[/b] [b]~[/b]Task() { } [color=#999988][i]/** Checks if all dependencies are met and the task can run[/i][/color] [color=#999988][i]* @returns true if scheduler can run task, false otherwise[/i][/color] [color=#999988][i]*/[/i][/color] [b]virtual[/b] BOOL isReady() [b]=[/b] [color=#009999]0[/color]; [color=#999988][i]/// User task code goes here[/i][/color] [b]virtual[/b] [color=#445588][b]void[/b][/color] run() [b]=[/b] [color=#009999]0[/color]; [color=#999988][i]/// Checks whether the task is marked complete[/i][/color] BOOL complete() { [b]return[/b] m_complete; } [color=#999988][i]/** Sets the complete status of the task[/i][/color] [color=#999988][i]* @param v Completetion status[/i][/color] [color=#999988][i]*/[/i][/color] [color=#445588][b]void[/b][/color] setComplete(BOOL v) { m_complete [b]=[/b] v; } [b]private[/b][b]:[/b] [color=#999988][i]/// Flag indicating task completion[/i][/color] BOOL m_complete; }; [color=#999988][i]/// Runs Tasks across multiple threads on multiple cores[/i][/color] [b]class[/b] [color=#445588][b]TaskScheduler[/b][/color] { [b]public[/b][b]:[/b] [color=#999988][i]/** Create scheduler with default number of worker threads.[/i][/color] [color=#999988][i]* Default is 1 thread per 1 logical core (core or HT unit)[/i][/color] [color=#999988][i]*/[/i][/color] TaskScheduler(); [color=#999988][i]/** Create scheduler with specified number of worker threads[/i][/color] [color=#999988][i]* @param n Number of worker threads (must be less than MAX_THREADS)[/i][/color] [color=#999988][i]*/[/i][/color] TaskScheduler(U32 n); [color=#999988][i]/// Finishes all currently running tasks, then stops all worker threads.[/i][/color] [b]~[/b]TaskScheduler(); [color=#999988][i]/** Schedules a task for execection[/i][/color] [color=#999988][i]* The task is assigned to the first idle work thread if there is one;[/i][/color] [color=#999988][i]* otherwise, the task is put on the queue of a worker thread. Pushed tasks[/i][/color] [color=#999988][i]* execute immediately.[/i][/color] [color=#999988][i]*/[/i][/color] [color=#445588][b]void[/b][/color] pushTask(Task[b]*[/b] t); [color=#999988][i]/// Blocks execution until all worker threads have completed there tasks[/i][/color] [color=#445588][b]void[/b][/color] runUntilDone(); [color=#999988][i]/** Gets number of worker threads[/i][/color] [color=#999988][i]* @return Number of worker threads[/i][/color] [color=#999988][i]*/[/i][/color] U32 threadCount(); [b]private[/b][b]:[/b] U32 m_workerCount; WorkerThread[b]*[/b] m_workers[MAX_THREADS]; BOOL m_running; U32 m_nextPushIndex; [b]friend[/b] [b]class[/b] [color=#445588][b]WorkerThread[/b][/color]; }; [color=#999988][i]/** Implementation of a task-stealing worker thread for the TaskScheduler[/i][/color] [color=#999988][i]* WorkerThreads steal tasks from other WorkerThreads if they are out of tasks or[/i][/color] [color=#999988][i]* blocked by task dependencies. If a WorkerThread is truly out of tasks, it sleeps on a[/i][/color] [color=#999988][i]* condition variable until new tasks are available.[/i][/color] [color=#999988][i]*/[/i][/color] [b]class[/b] [color=#445588][b]WorkerThread[/b][/color] { [b]public[/b][b]:[/b] [color=#999988][i]/** Creates a new worker thread--only called by a TaskScheduler[/i][/color] [color=#999988][i]* The system thread is started and enters idle, waiting on a condition variable.[/i][/color] [color=#999988][i]* @param scheduler Parent TaskScheduler[/i][/color] [color=#999988][i]*/[/i][/color] WorkerThread(TaskScheduler[b]*[/b] scheduler); [color=#999988][i]/// Deletes underlying system thread[/i][/color] [b]~[/b]WorkerThread(); [color=#999988][i]/** Adds a task to the local task pool[/i][/color] [color=#999988][i]* This method is called by the TaskScheduler from the main thread and uses[/i][/color] [color=#999988][i]* a spinlock to access task queue. Execution of the task can begin immediately after[/i][/color] [color=#999988][i]* the lock mutex is released.[/i][/color] [color=#999988][i]* @param t Task to add to queue[/i][/color] [color=#999988][i]*/[/i][/color] [color=#445588][b]void[/b][/color] pushTask(Task[b]*[/b] t); [color=#999988][i]/** Checks whether the worker thread is currently idling[/i][/color] [color=#999988][i]* @return true if idling, false otherwise[/i][/color] [color=#999988][i]*/[/i][/color] BOOL idling(); [color=#999988][i]/** Blocks execution until the worker thread has completed all tasks in its queue[/i][/color] [color=#999988][i]* and can no longer find tasks to steal[/i][/color] [color=#999988][i]*/[/i][/color] [color=#445588][b]void[/b][/color] blockUntilDone(); [color=#999988][i]/** Wakes the thread and joins execution[/i][/color] [color=#999988][i]* TaskScheduler must have m_running = false or method will block indefinitely[/i][/color] [color=#999988][i]*/[/i][/color] [color=#445588][b]void[/b][/color] stop(); [b]private[/b][b]:[/b] [color=#445588][b]void[/b][/color] thread_proc(); BOOL run(); [color=#445588][b]void[/b][/color] idle(); BOOL steal(); BOOL stealFromWorker(WorkerThread[b]*[/b] wt); TaskScheduler[b]*[/b] m_scheduler; boost[b]::[/b][b]thread[/b][b]*[/b] m_internalThread; boost[b]::[/b]mutex m_workerMutex; boost[b]::[/b]mutex m_idleMutex; boost[b]::[/b]condition_variable m_wakeUp; boost[b]::[/b]condition_variable m_done; std[b]::[/b]queue[b]<[/b]Task[b]*>[/b] m_tasks; U32 m_taskCount; BOOL m_idling; BOOL m_dependencyBlocked; }; [color=#999988][i]//// HEADERS ////[/i][/color] [color=#999988][i]//// IMPLEMENTATIONS ////[/i][/color] [color=#999988][i]/// TASK SCHEDULER ///[/i][/color] TaskScheduler[b]::[/b]TaskScheduler() { m_running [b]=[/b] [b]true[/b]; [color=#999988][i]// use number of 'logical cores' as default number of worker threads[/i][/color] [color=#999988][i]// on CPUs with Hyperthreading, each core counts as 2[/i][/color] m_workerCount [b]=[/b] boost[b]::[/b][b]thread[/b][b]::[/b]hardware_concurrency(); [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] m_workerCount; i[b]++[/b]) m_workers[i] [b]=[/b] [b]new[/b] WorkerThread([b]this[/b]); m_nextPushIndex [b]=[/b] [color=#009999]0[/color]; } TaskScheduler[b]::[/b]TaskScheduler(U32 n) { m_workerCount [b]=[/b] n; [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] m_workerCount; i[b]++[/b]) m_workers[i] [b]=[/b] [b]new[/b] WorkerThread([b]this[/b]); m_nextPushIndex [b]=[/b] [color=#009999]0[/color]; } TaskScheduler[b]::~[/b]TaskScheduler() { m_running [b]=[/b] [b]false[/b]; [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] m_workerCount; i[b]++[/b]) m_workers[i][b]->[/b]stop(); } [color=#445588][b]void[/b][/color] TaskScheduler[b]::[/b]pushTask(Task[b]*[/b] t) { [color=#999988][i]// try to find an idling worker thread[/i][/color] [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] m_workerCount; i[b]++[/b]) { [b]if[/b] (m_workers[i][b]->[/b]idling()) { m_workers[i][b]->[/b]pushTask(t); [b]return[/b]; } } [color=#999988][i]// push it onto the next worker thread round-robin style[/i][/color] [color=#999988][i]// if there isn't an idling thread[/i][/color] m_workers[m_nextPushIndex[b]++[/b]][b]->[/b]pushTask(t); [b]if[/b] (m_nextPushIndex [b]>=[/b] m_workerCount) m_nextPushIndex [b]=[/b] [color=#009999]0[/color]; } [color=#445588][b]void[/b][/color] TaskScheduler[b]::[/b]runUntilDone() { [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] m_workerCount; i[b]++[/b]) m_workers[i][b]->[/b]blockUntilDone(); } U32 TaskScheduler[b]::[/b]threadCount() { [b]return[/b] m_workerCount; } [color=#999988][i]/// TASK SCHEDULER ///[/i][/color] [color=#999988][i]/// WORKER THREAD ///[/i][/color] WorkerThread[b]::[/b]WorkerThread(TaskScheduler[b]*[/b] scheduler) { m_scheduler [b]=[/b] scheduler; m_taskCount [b]=[/b] [color=#009999]0[/color]; m_idling [b]=[/b] [b]false[/b]; m_dependencyBlocked [b]=[/b] [b]false[/b]; [color=#999988][i]// creates system thread[/i][/color] [color=#999988][i]// thread_proc begins execution immediately[/i][/color] m_internalThread [b]=[/b] [b]new[/b] boost[b]::[/b][b]thread[/b]([b]&[/b]WorkerThread[b]::[/b]thread_proc, [b]this[/b]); } WorkerThread[b]::~[/b]WorkerThread() { [b]delete[/b] m_internalThread; } [color=#445588][b]void[/b][/color] WorkerThread[b]::[/b]pushTask(Task[b]*[/b] t) { m_workerMutex.lock(); m_tasks.push(t); m_taskCount[b]++[/b]; m_workerMutex.unlock(); m_idleMutex.lock(); m_idling [b]=[/b] [b]false[/b]; m_idleMutex.unlock(); [color=#999988][i]// wake up the thread if idling[/i][/color] m_wakeUp.notify_all(); } BOOL WorkerThread[b]::[/b]idling() { boost[b]::[/b]lock_guard[b]<[/b]boost[b]::[/b]mutex[b]>[/b] lock(m_idleMutex); [b]return[/b] m_idling; } [color=#445588][b]void[/b][/color] WorkerThread[b]::[/b]blockUntilDone() { [color=#999988][i]// the m_done condition variable is notified when the thread enters idle,[/i][/color] [color=#999988][i]// which only happens when it is out of tasks and can't find any to steal[/i][/color] boost[b]::[/b]unique_lock[b]<[/b]boost[b]::[/b]mutex[b]>[/b] lock(m_idleMutex); [b]while[/b]([b]![/b]m_idling) m_done.wait(lock); } [color=#445588][b]void[/b][/color] WorkerThread[b]::[/b]thread_proc() { [color=#999988][i]// always idle when thread first starts because[/i][/color] [color=#999988][i]// not all other worker threads are gauranteed to[/i][/color] [color=#999988][i]// be initialized yet, and steal() relies on that.[/i][/color] [color=#999988][i]// Also, there shouldn't be any tasks yet anyway.[/i][/color] idle(); [b]for[/b](;;) { [b]if[/b] ([b]![/b]m_scheduler[b]->[/b]m_running) [b]break[/b]; [color=#999988][i]// try to run a task. If there are not tasks available,[/i][/color] [color=#999988][i]// try to steal tasks. If there are no tasks to steal[/i][/color] [color=#999988][i]// and the task queue is empty, then idle. If there are[/i][/color] [color=#999988][i]// dependency-blocked tasks, the thread does not idle.[/i][/color] [b]if[/b] ([b]![/b]run()) { [b]if[/b] ([b]![/b]steal() [b]&&[/b] m_taskCount [b]<=[/b] [color=#009999]0[/color]) idle(); } } } BOOL WorkerThread[b]::[/b]run() { [b]if[/b] (m_taskCount [b]<=[/b] [color=#009999]0[/color]) [b]return[/b] [b]false[/b]; [color=#999988][i]// Try to find a task to run. If all tasks in the queue[/i][/color] [color=#999988][i]// have been checked, then return false so we can try to steal.[/i][/color] [color=#999988][i]// If we have tasks waiting on dependencies, set m_dependencyBlocked[/i][/color] [color=#999988][i]// so other WorkerThreads know not to steal from this one.[/i][/color] m_workerMutex.lock(); m_dependencyBlocked [b]=[/b] [b]false[/b]; U32 numPops [b]=[/b] [color=#009999]0[/color]; Task[b]*[/b] t; [b]while[/b] (numPops [b]<[/b] m_taskCount) { t [b]=[/b] m_tasks.front(); m_tasks.pop(); [b]if[/b] (t[b]->[/b]isReady()) { m_taskCount[b]--[/b]; m_workerMutex.unlock(); t[b]->[/b]run(); [b]return[/b] [b]true[/b]; } numPops[b]++[/b]; m_tasks.push(t); } [b]if[/b] (numPops [b]>[/b] [color=#009999]0[/color]) m_dependencyBlocked [b]=[/b] [b]true[/b]; m_workerMutex.unlock(); [b]return[/b] [b]false[/b]; } [color=#445588][b]void[/b][/color] WorkerThread[b]::[/b]idle() { m_idleMutex.lock(); [b]if[/b] (m_taskCount [b]>[/b] [color=#009999]0[/color]) { m_idleMutex.unlock(); [b]return[/b]; } m_idling [b]=[/b] [b]true[/b]; m_idleMutex.unlock(); [color=#999988][i]// If another thread is waiting on us to finish execution,[/i][/color] [color=#999988][i]// notify that this thread is done now[/i][/color] [color=#999988][i]// Used by blockUntilDone()[/i][/color] m_done.notify_all(); boost[b]::[/b]unique_lock[b]<[/b]boost[b]::[/b]mutex[b]>[/b] lock(m_idleMutex); [b]while[/b] (m_idling) m_wakeUp.wait(lock); } BOOL WorkerThread[b]::[/b]steal() { [color=#999988][i]// check each worker thread for extra work[/i][/color] U32 workerCount [b]=[/b] m_scheduler[b]->[/b]m_workerCount; [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] workerCount; i[b]++[/b]) { WorkerThread[b]*[/b] wt [b]=[/b] m_scheduler[b]->[/b]m_workers[i]; [b]if[/b] (wt [b]==[/b] [b]this[/b]) [b]continue[/b]; [b]if[/b] (stealFromWorker(wt)) [b]return[/b] [b]true[/b]; } [b]return[/b] [b]false[/b]; } BOOL WorkerThread[b]::[/b]stealFromWorker(WorkerThread[b]*[/b] wt) { wt[b]->[/b]m_workerMutex.lock(); [color=#999988][i]// steal half of the other thread's tasks,[/i][/color] [color=#999988][i]// rounding up. If they don't have tasks[/i][/color] [color=#999988][i]// (the check in steal() is not guaranteed because,[/i][/color] [color=#999988][i]// it does not lock the mutex), then return false so[/i][/color] [color=#999988][i]// we can try another worker thread.[/i][/color] U32 numToSteal; U32 taskCount [b]=[/b] wt[b]->[/b]m_taskCount; [b]if[/b] (wt[b]->[/b]m_taskCount [b]<=[/b] [color=#009999]0[/color] [b]||[/b] wt[b]->[/b]m_dependencyBlocked) { wt[b]->[/b]m_workerMutex.unlock(); [b]return[/b] [b]false[/b]; } [b]else[/b] numToSteal [b]=[/b] (wt[b]->[/b]m_taskCount [b]+[/b] [color=#009999]1[/color]) [b]/[/b] [color=#009999]2[/color]; m_workerMutex.lock(); [b]for[/b] (U32 i [b]=[/b] [color=#009999]0[/color]; i [b]<[/b] numToSteal; i[b]++[/b]) { m_tasks.push(wt[b]->[/b]m_tasks.front()); wt[b]->[/b]m_tasks.pop(); wt[b]->[/b]m_taskCount[b]--[/b]; m_taskCount[b]++[/b]; } wt[b]->[/b]m_workerMutex.unlock(); m_workerMutex.unlock(); [b]return[/b] [b]true[/b]; } [color=#445588][b]void[/b][/color] WorkerThread[b]::[/b]stop() { m_idleMutex.lock(); m_idling [b]=[/b] [b]false[/b]; m_idleMutex.unlock(); m_wakeUp.notify_all(); m_internalThread[b]->[/b]join(); } [color=#999988][i]/// WORKER THREAD ///[/i][/color] [color=#999988][i]//// IMPLEMENTATIONS ////[/i][/color] [/source]
  4. I've been working on a simple, cross-platform task/job scheduler, and I would like some help reviewing the code. Any feedback on general pitfalls or problems is appreciated! I've posted the code on github, and below: [url="https://gist.github.com/2625036"]https://gist.github.com/2625036[/url] [code] /// This is not written to compile. I just copied everything into one file to make viewing easy /// Here is a sample usage (task definitions and implementations omitted) int main() { TaskScheduler scheduler; MyTaskA taskA; MyTaskB taskB; BOOL running = true while (running) { scheduler.pushTask(&taskA); scheduler.pushTask(&taskB); scheduler.runUntilDone(); } } #include "boost/thread.hpp" class Task; class TaskScheduler; class WorkerThread; //// HEADERS //// /// Abstract interface for all tasks run by scheduler class Task { public: virtual ~Task() { } /** Checks if all dependencies are met and the task can run * @returns true if scheduler can run task, false otherwise */ virtual BOOL isReady() = 0; /// User task code goes here virtual void run() = 0; /// Checks whether the task is marked complete BOOL complete() { return m_complete; } /** Sets the complete status of the task * @param v Completetion status */ void setComplete(BOOL v) { m_complete = v; } private: /// Flag indicating task completion BOOL m_complete; }; /// Runs Tasks across multiple threads on multiple cores class TaskScheduler { public: /** Create scheduler with default number of worker threads. * Default is 1 thread per 1 logical core (core or HT unit) */ TaskScheduler(); /** Create scheduler with specified number of worker threads * @param n Number of worker threads (must be less than MAX_THREADS) */ TaskScheduler(U32 n); /// Finishes all currently running tasks, then stops all worker threads. ~TaskScheduler(); /** Schedules a task for execection * The task is assigned to the first idle work thread if there is one; * otherwise, the task is put on the queue of a worker thread. Pushed tasks * execute immediately. */ void pushTask(Task* t); /// Blocks execution until all worker threads have completed there tasks void runUntilDone(); /** Gets number of worker threads * @return Number of worker threads */ U32 threadCount(); private: U32 m_workerCount; WorkerThread* m_workers[MAX_THREADS]; BOOL m_running; U32 m_nextPushIndex; friend class WorkerThread; }; /** Implementation of a task-stealing worker thread for the TaskScheduler * WorkerThreads steal tasks from other WorkerThreads if they are out of tasks or * blocked by task dependencies. If a WorkerThread is truly out of tasks, it sleeps on a * condition variable until new tasks are available. */ class WorkerThread { public: /** Creates a new worker thread--only called by a TaskScheduler * The system thread is started and enters idle, waiting on a condition variable. * @param scheduler Parent TaskScheduler */ WorkerThread(TaskScheduler* scheduler); /// Deletes underlying system thread ~WorkerThread(); /** Adds a task to the local task pool * This method is called by the TaskScheduler from the main thread and uses * a spinlock to access task queue. Execution of the task can begin immediately after * the lock mutex is released. * @param t Task to add to queue */ void pushTask(Task* t); /** Checks whether the worker thread is currently idling * @return true if idling, false otherwise */ BOOL idling(); /** Blocks execution until the worker thread has completed all tasks in its queue * and can no longer find tasks to steal */ void blockUntilDone(); /** Wakes the thread and joins execution * TaskScheduler must have m_running = false or method will block indefinitely */ void stop(); private: void thread_proc(); BOOL run(); void idle(); BOOL steal(); BOOL stealFromWorker(WorkerThread* wt); TaskScheduler* m_scheduler; boost::thread* m_internalThread; boost::mutex m_workerMutex; boost::condition_variable m_wakeUp; boost::condition_variable m_done; std::queue<Task*> m_tasks; U32 m_taskCount; BOOL m_idling; BOOL m_dependencyBlocked; }; //// HEADERS //// //// IMPLEMENTATIONS //// /// TASK SCHEDULER /// TaskScheduler::TaskScheduler() { m_running = true; // use number of 'logical cores' as default number of worker threads // on CPUs with Hyperthreading, each core counts as 2 m_workerCount = boost::thread::hardware_concurrency(); for (U32 i = 0; i < m_workerCount; i++) m_workers[i] = new WorkerThread(this); m_nextPushIndex = 0; } TaskScheduler::TaskScheduler(U32 n) { m_workerCount = n; for (U32 i = 0; i < m_workerCount; i++) m_workers[i] = new WorkerThread(this); m_nextPushIndex = 0; } TaskScheduler::~TaskScheduler() { m_running = false; for (U32 i = 0; i < m_workerCount; i++) m_workers[i]->stop(); } void TaskScheduler::pushTask(Task* t) { // try to find an idling worker thread for (U32 i = 0; i < m_workerCount; i++) { if (m_workers[i]->idling()) { m_workers[i]->pushTask(t); return; } } // push it onto the next worker thread round-robin style // if there isn't an idling thread m_workers[m_nextPushIndex++]->pushTask(t); if (m_nextPushIndex >= m_workerCount) m_nextPushIndex = 0; } void TaskScheduler::runUntilDone() { for (U32 i = 0; i < m_workerCount; i++) m_workers[i]->blockUntilDone(); } U32 TaskScheduler::threadCount() { return m_workerCount; } /// TASK SCHEDULER /// /// WORKER THREAD /// WorkerThread::WorkerThread(TaskScheduler* scheduler) { m_scheduler = scheduler; m_taskCount = 0; m_idling = false; m_dependencyBlocked = false; // creates system thread // thread_proc begins execution immediately m_internalThread = new boost::thread(&WorkerThread::thread_proc, this); } WorkerThread::~WorkerThread() { delete m_internalThread; } void WorkerThread::pushTask(Task* t) { while (!m_workerMutex.try_lock()); m_tasks.push(t); m_taskCount++; m_idling = false; m_workerMutex.unlock(); // wake up the thread if idling m_wakeUp.notify_all(); } BOOL WorkerThread::idling() { return m_idling; } void WorkerThread::blockUntilDone() { if (m_idling) return; // the m_done condition variable is notified when the thread enters idle, // which only happens when it is out of tasks and can't find any to steal boost::unique_lock<boost::mutex> lock(m_workerMutex); while(!m_idling) m_done.wait(lock); } void WorkerThread::thread_proc() { // always idle when thread first starts because // not all other worker threads are gauranteed to // be initialized yet, and steal() relies on that. // Also, there shouldn't be any tasks yet anyway. idle(); for(;;) { if (!m_scheduler->m_running) break; // try to run a task. If there are not tasks available, // try to steal tasks. If there are no tasks to steal // and the task queue is empty, then idle. If there are // dependency-blocked tasks, the thread does not idle. if (!run()) { if (!steal() && m_taskCount <= 0) idle(); } } } BOOL WorkerThread::run() { if (m_taskCount <= 0) return false; // Try to find a task to run. If all tasks in the queue // have been checked, then return false so we can try to steal. // If we have tasks waiting on dependencies, set m_dependencyBlocked // so other WorkerThreads know not to steal from this one. m_workerMutex.lock(); m_dependencyBlocked = false; U32 numPops = 0; Task* t; while (numPops < m_taskCount) { t = m_tasks.front(); m_tasks.pop(); if (t->isReady()) { m_taskCount--; m_workerMutex.unlock(); t->run(); return true; } numPops++; m_tasks.push(t); } if (numPops > 0) m_dependencyBlocked = true; m_workerMutex.unlock(); return false; } void WorkerThread::idle() { m_workerMutex.lock(); m_idling = true; m_workerMutex.unlock(); // If another thread is waiting on us to finish execution, // notify that this thread is done now // Used by blockUntilDone() m_done.notify_all(); boost::unique_lock<boost::mutex> lock(m_workerMutex); while (m_idling) m_wakeUp.wait(lock); } BOOL WorkerThread::steal() { // check each worker thread for extra work U32 workerCount = m_scheduler->m_workerCount; for (U32 i = 0; i < workerCount; i++) { WorkerThread* wt = m_scheduler->m_workers[i]; if (wt == this || wt->m_dependencyBlocked) continue; if (wt->m_taskCount > 0) { if (stealFromWorker(wt)) return true; } } return false; } BOOL WorkerThread::stealFromWorker(WorkerThread* wt) { while (!wt->m_workerMutex.try_lock()); // steal half of the other thread's tasks, // rounding up. If they don't have tasks // (the check in steal() is not guaranteed because, // it does not lock the mutex), then return false so // we can try another worker thread. U32 numToSteal; U32 taskCount = wt->m_taskCount if (wt->m_taskCount <= 0) { wt->m_workerMutex.unlock(); return false; } else numToSteal = (wt->m_taskCount + 1) / 2; m_workerMutex.lock(); for (U32 i = 0; i < numToSteal; i++) { m_tasks.push(wt->m_tasks.front()); wt->m_tasks.pop(); wt->m_taskCount--; m_taskCount++; } wt->m_workerMutex.unlock(); m_workerMutex.unlock(); return true; } void WorkerThread::stop() { m_workerMutex.lock(); m_idling = false; m_workerMutex.unlock(); m_wakeUp.notify_all(); m_internalThread->join(); } /// WORKER THREAD /// //// IMPLEMENTATIONS //// [/code]
  5. I need to cast a ray from the position of a user's touch on a iOS device onto a plane on the XZ axis in OpenGL ES. I have code to build the ray points from the touch position and viewport size, and code to generate the inverse view*proj. Also, I have some code to calculate the position on the plane where it does intersect, but that part isn't working so well. Any help with it is much appreciated! CGPoint p = [[touches anyObject] locationInView:self.view]; cml::vector4f r1(p.x / windowSize.width / windowSize.height, p.y, 0.0f, 1.0f); cml::vector4f r2(p.x / windowSize.width, p.y / windowSize.height, 1.0f, 1.0f); // unproject the ray points cml::matrix44f_c invViewProj = orbitCam.viewMat * ((OpenGLView*)self.view).projMat; invViewProj = cml::inverse(invViewProj); r1 = invViewProj * r1; r2 = invViewProj * r2; cml::vector3f R1(r1[0], r1[1], r1[2]); cml::vector3f R2(r2[0], r2[1], r2[2]); cml::vector3f dir = cml::normalize(R2 - R1); cml::vector3f V1(1.0f, 0.0f, 0.0f); cml::vector3f V2(0.0f, 0.0f, 1.0f); cml::vector3f V3 = cml::cross(V1, V2); cml::vector3f vRotRay1(cml::dot(V1, R1 - V1), cml::dot(V2, R1 - V1), cml::dot(V3, R1 - V1)); cml::vector3f vRotRay2(cml::dot(V1, R2 - V1), cml::dot(V2, R2 - V1), cml::dot(V3, R2 - V1)); float percent = vRotRay1[2] / (vRotRay2[2] - vRotRay1[2]); cml::vector3f intersect = vRotRay1 + (vRotRay1 - vRotRay2) * percent;
  6. Hello all, I am rendering brush strokes in an application to a secondary framebuffer to later render them onto the main framebuffer. However, I am getting weird behavior when clearing the framebuffer. The alpha channel doesn't appear to clear to 0. When I render it, the texture is completely white with alpha of 1.0 it seems. Code below: // clears the brush strokes from the secondary framebuffer - (void) clearBrushStrokes { glEnable(GL_BLEND); glBindFramebufferOES(GL_FRAMEBUFFER_OES, renderFrameBuffer); glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); } // snippet of creation of the render texture glGenFramebuffersOES(1, &renderFrameBuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, renderFrameBuffer); glGenTextures(1, &renderTexture); glBindTexture(GL_TEXTURE_2D, renderTexture); NSInteger deviceSize = [iChalkDrawingModel sharediChalkDrawingModel].powerOfTwoSize; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, deviceSize, deviceSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, renderTexture, 0);
  7. Some inspiration: GameSphere
  8. When I was doing engine development for the iPhone, I used the Configurable Math Library: http://cmldev.net It works well, however I don't know whether it will be updated to make use of the iPhone/iPad vector/SIMD processors. If you want some really optimized floating point vector operations, Apple just released a ported version of vecLib with the SDK version 4.
  9. You must load a power of 2 texture to avoid distortion or memory issues. However, you can render any shape portion out of that texture. You use the texture mapping coordinates on your vertices to specify what portion to render, whether it be power of 2 or not.
  10. I am trying to implement a camera system that ties into the scenegraph. Every object on the scenenode is passed a matrix from its parent node and multiplies its own matrix to that. Then the contents are rendered. The problem I have is setting up an 2D camera using the scene node's transformation to build the view matrix. From the scene node I have a transformation that translates, rotates, and scales. My world space before the view matrix is setup with (0, 0) at the top left corner. I need the camera to rotate about the center and zoom about the center. Every combination of transforms I think should work does not work yet. How do I set the view matrix from the scenegraph transform?
  11. I am currently working on a 2D scenegraph system. I have a scene node class that contains a Transform structure describing translation, scaling, and rotation. Child nodes of scene nodes inherit their parents' transformation and then transform relative to that. Translation and scaling are relatively easy to do: just addition and multiplication of the vectors. However, inheriting rotation is difficult. Each node has a rotation relative to its own translation. How do I make child node's calculate their local transformation taking into account the parent's rotation and rotation about their local translations?
  12. I wonder if this is another one of those joke posts? I see one every once in a while. Someone will post a terrible cliche post like this just to get a kick (except usually about making a WoW beater). Problem is... sometimes people don't realize it's a joke. I can only hope this post was...
  13. Add the current offset or scroll position to the mouse y value then divide by 64. g_ptTilePos.y = (y + offsetY) / TILE_HEIGHT;
  14. Google it... my favorite source is http://reinerstileset.4players.de/environmentE.html
  15. Usually you will use the PC parallel port. Here is a site showing how to connect an LED to the parallel port http://ashishrd.blogspot.com/2006/11/controlling-leds-with-parallel-port.html EDIT: Or you can use a TTL level converter(Max233 i recommend) and use a shift register on the serial port.