File: RenderMgr.cpp

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (157 lines) | stat: -rw-r--r-- 3,679 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "stdafx.h"
#include "RenderMgr.h"
#include "Exception.h"
#include "Painter.h"
#include "LibData.h"
#include "Resource.h"
#include "Core/Array.h"
#include "TextMgr.h"

namespace gui {

	RenderMgr::RenderMgr() : exiting(false) {
		painters = new (this) Set<Painter *>();
		waitEvent = new (this) Event();
		exitSema = new (this) Sema(0);

		try {
			device = Device::create(engine());
			textMgr = device->createTextMgr();
		} catch (const storm::Exception *e) {
			PLN("Error while initializing rendering: " << e);
			throw;
		} catch (const ::Exception &e) {
			PLN("Error while initializing rendering: " << e);
			throw;
		}

		idMgr = new IdMgr();
	}

	Nat RenderMgr::allocId() {
		if (idMgr)
			return idMgr->alloc();
		else
			return 0;
	}

	void RenderMgr::freeId(Nat id) {
		// This is to ensure that we don't crash during shutdown.
		if (idMgr)
			idMgr->free(id);
	}

	Surface *RenderMgr::attach(Painter *painter, Handle window) {
		Surface *s = device->createSurface(window);
		if (s)
			painters->put(painter);
		return s;
	}

	void RenderMgr::detach(Painter *painter) {
		// Painter::destroy calls detach, which will mess up the loop in "terminate" unless we're careful here.
		if (!exiting)
			painters->remove(painter);
	}

	void RenderMgr::terminate() {
		exiting = true;

		waitEvent->set();
		exitSema->down();

		// Destroy all resources in all painters.
		for (Set<Painter *>::Iter i = painters->begin(), e = painters->end(); i != e; ++i) {
			i.v()->destroy();
		}

		delete textMgr;
		textMgr = null;

		delete device;
		device = null;

		delete idMgr;
		idMgr = null;
	}

	void RenderMgr::main() {
		// Note: Not called if SINGLE_THREADED_UI is defined.
		Array<Painter *> *toRedraw = new (this) Array<Painter *>();

		while (!exiting) {
			// Empty the array, reuse the storage.
			for (Nat i = 0; i < toRedraw->count(); i++)
				toRedraw->at(i) = null;

			// Figure out which we need to redraw this frame. Copy them since others may modify the
			// hash set as soon as we do UThread::leave.
			Nat pos = 0;
			for (Set<Painter *>::Iter i = painters->begin(), e = painters->end(); i != e; ++i) {
				Painter *p = i.v();
				if (p->continuous && p->ready()) {
					if (pos >= toRedraw->count())
						toRedraw->push(p);
					else
						toRedraw->at(pos++) = p;
				}
			}

			Bool any = false;
			for (Nat i = 0; i < toRedraw->count(); i++) {
				if (toRedraw->at(i) == null)
					continue;
				any = true;

				// TODO: We probably want to wait for VSync once only, and not block this thread while doing so.
				// Note: It seems from the documentation for 'IDXGISwapChain::Present' that it schedules a buffer
				// swap but does not block until the next call to 'Present'. If so, we do not have to worry.
				try {
					toRedraw->at(i)->doRepaint(true, false);
				} catch (const storm::Exception *e) {
					PLN(L"Error while rendering:\n" << e);
				} catch (const ::Exception &e) {
					PLN(L"Error while rendering:\n" << e);
				} catch (...) {
					PLN(L"Unknown error while rendering.");
				}
				os::UThread::leave();
			}

			if (!any)
				waitEvent->wait();
			waitEvent->clear();
		}

		exitSema->up();
	}

	void RenderMgr::painterReady() {
		waitEvent->set();
	}

	RenderMgr *renderMgr(EnginePtr e) {
		RenderMgr *&r = gui::renderData(e.v);
		if (!r)
			r = new (e.v) RenderMgr();
		return r;
	}

	os::Thread spawnRenderThread(Engine &e) {
		struct Wrap {
			Engine &e;
			Wrap(Engine &e) : e(e) {}

			void run() {
				RenderMgr *m = gui::renderMgr(e);
				m->main();
				delete this;
			}
		};

		Wrap *wrap = new Wrap(e);
		util::Fn<void, void> fn(wrap, &Wrap::run);
		return os::Thread::spawn(fn, runtime::threadGroup(e));
	}

}