summaryrefslogtreecommitdiff
path: root/src/modules/montage/montage.c
blob: 697c28d589f4b70e172a2c02638df10863701d80 (plain)
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <math.h>
#include <stdlib.h>
#include <time.h>

#include "fb.h"
#include "rototiller.h"
#include "settings.h"
#include "util.h"

/* Copyright (C) 2019 - Vito Caputo <vcaputo@pengaru.com> */

typedef struct montage_context_t {
	const rototiller_module_t	**modules;
	void				**contexts;
	size_t				n_modules;
	unsigned			n_cpus;
} montage_context_t;

static void setup_next_module(montage_context_t *ctxt);
static void * montage_create_context(unsigned num_cpus);
static void montage_destroy_context(void *context);
static void montage_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter);
static void montage_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment);


rototiller_module_t	montage_module = {
	.create_context = montage_create_context,
	.destroy_context = montage_destroy_context,
	.prepare_frame = montage_prepare_frame,
	.render_fragment = montage_render_fragment,
	.name = "montage",
	.description = "Rototiller montage",
	.author = "Vito Caputo <vcaputo@pengaru.com>",
	.license = "GPLv2",
};


static void * montage_create_context(unsigned num_cpus)
{
	const rototiller_module_t	**modules, *rtv_module, *pixbounce_module, *stars_module;
	size_t				n_modules;
	montage_context_t		*ctxt;

	ctxt = calloc(1, sizeof(montage_context_t));
	if (!ctxt)
		return NULL;

	rototiller_get_modules(&modules, &n_modules);

	ctxt->modules = calloc(n_modules, sizeof(rototiller_module_t *));
	if (!ctxt->modules) {
		free(ctxt);

		return NULL;
	}

	rtv_module = rototiller_lookup_module("rtv");
	pixbounce_module = rototiller_lookup_module("pixbounce");
	stars_module = rototiller_lookup_module("stars");

	for (size_t i = 0; i < n_modules; i++) {
		const rototiller_module_t	*module = modules[i];

		if (module == &montage_module ||	/* prevents recursion */
		    module == rtv_module ||		/* also prevents recursion, rtv can run montage */
		    module == pixbounce_module ||	/* temporarily broken in montage */
		    module == stars_module)		/* temporarily broken in montage */
			continue;

		ctxt->modules[ctxt->n_modules++] = module;
	}

	ctxt->n_cpus = num_cpus;

	ctxt->contexts = calloc(ctxt->n_modules, sizeof(void *));
	if (!ctxt->contexts) {
		free(ctxt);

		return NULL;
	}

	for (size_t i = 0; i < ctxt->n_modules; i++) {
		const rototiller_module_t	*module = ctxt->modules[i];

		if (module->create_context)	/* FIXME errors */
			ctxt->contexts[i] = module->create_context(num_cpus);
	}

	return ctxt;
}


static void montage_destroy_context(void *context)
{
	montage_context_t	*ctxt = context;

	for (int i = 0; i < ctxt->n_modules; i++) {
		const rototiller_module_t	*module = ctxt->modules[i];

		if (!ctxt->contexts[i])
			continue;

		 module->destroy_context(ctxt->contexts[i]);
	}

	free(ctxt->contexts);
	free(ctxt->modules);
	free(ctxt);
}


/* The fragmenter in montage is serving double-duty:
 * 1. it divides the frame into subfragments for threaded rendering
 * 2. it determines which modules will be rendered where via fragment->number
 */
static int montage_fragmenter(void *context, const fb_fragment_t *fragment, unsigned number, fb_fragment_t *res_fragment)
{
	montage_context_t	*ctxt = context;
	float			root = sqrtf((float)ctxt->n_modules);
	int			ret;

	ret = fb_fragment_tile_single(fragment, fragment->frame_height / root, number, res_fragment);
	if (!ret)
		return 0;

	/* as these tiles are frames of their own rather than subfragments, override these values */
	res_fragment->x = res_fragment->y = 0;
	res_fragment->frame_width = res_fragment->width;
	res_fragment->frame_height = res_fragment->height;

	return ret;
}



static void montage_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter)
{
	montage_context_t	*ctxt = context;

	*res_fragmenter = montage_fragmenter;
}


static void montage_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment)
{
	montage_context_t		*ctxt = context;
	const rototiller_module_t	*module = ctxt->modules[fragment->number];

	if (fragment->number >= ctxt->n_modules) {

		fb_fragment_zero(fragment);

		return;
	}


	/* since we're *already* in a threaded render of tiles, no further
	 * threading within the montage tiles is desirable, so the per-module
	 * render is done explicitly serially here in an open-coded ad-hoc
	 * fashion for now. FIXME TODO: move this into rototiller.c
	 */
	if (module->prepare_frame) {
		rototiller_fragmenter_t	unused;

		/* XXX FIXME: ignoring the fragmenter here is a violation of the module API,
		 * rototiller.c should have a module render interface with explicit non-threading
		 * that still does all the necessary fragmenting as needed.
		 *
		 * Today, I can get away with this, because montage is the only module that's
		 * sensitive to this aspect of the API and it skips itself.
		 */

		module->prepare_frame(ctxt->contexts[fragment->number], 1, fragment, &unused);
	}

	if (module->render_fragment)
		module->render_fragment(ctxt->contexts[fragment->number], 0, fragment);
}
© All Rights Reserved