2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /**********************************************
22 * Compute the tiling for a texture dimension *
23 **********************************************/
26 * To test the tiling algorithm:
27 * $ cc -DSTANDALONE $(pkg-config --libs --cflags gtk+-2.0) \
28 * -Iinclude tiling.c -o tiling
37 #include <stdio.h> /* printf() */
38 #include <stdlib.h> /* atoi() */
48 #define MAX_TEXTURE_SIZE 2048
51 #define MAX_TEXTURE_SIZE (rt->max_texture_size)
55 * glTexImage2D(3G): All implementations support texture images
56 * that are at least 64 texels (wide|high).
58 #define MIN_TEXTURE_SIZE 64
60 #define ARRAY_LENGTH (order(MAX_TEXTURE_SIZE) + 1)
64 static G_GNUC_CONST gint
order(gint p
)
77 /* Smallest power of two bigger than p */
78 static G_GNUC_CONST gint
p2(gint p
)
83 static struct tiles
*new_tiles(void)
85 struct tiles
*tiles
= g_new0(struct tiles
, 1);
86 tiles
->array
= g_new0(gint
, ARRAY_LENGTH
);
90 void destroy_tiles(struct tiles
*tiles
)
98 #define MORE_WASTE_THAN(a, b) ((a) - (b) > MIN_TEXTURE_SIZE)
100 static G_GNUC_CONST gint
try_single_texture(gint dim
)
104 if (dim
<= MIN_TEXTURE_SIZE
)
105 return MIN_TEXTURE_SIZE
;
107 if (dim
> MAX_TEXTURE_SIZE
)
113 if (MORE_WASTE_THAN(dim
- low
, high
- dim
))
119 #define ADD_TILES(nb, size) (dim -= add_tiles(tiles, (nb), (size)))
120 #define ADD_TILE(size) ADD_TILES(1, (size))
122 static gint
add_tiles(struct tiles
*tiles
, gint nb
, gint size
)
124 tiles
->array
[order(size
)] += nb
;
125 tiles
->nb_tiles
+= nb
;
126 return nb
* (size
- 1);
129 static struct tiles
*make_tiles_rec(gint dim
, gboolean can_be_single
)
132 struct tiles
*tiles_low
, *tiles_high
;
133 struct tiles
*tiles
= new_tiles();
135 if (dim
> 0 && can_be_single
) {
136 gint single
= try_single_texture(dim
);
142 if (dim
>= MAX_TEXTURE_SIZE
) {
143 ADD_TILES(dim
/ MAX_TEXTURE_SIZE
, MAX_TEXTURE_SIZE
);
147 if (dim
<= MIN_TEXTURE_SIZE
) {
148 ADD_TILE(MIN_TEXTURE_SIZE
);
155 tiles_low
= make_tiles_rec(dim
- low
+ 1, FALSE
);
156 tiles_high
= make_tiles_rec(dim
- high
+ 1, FALSE
);
158 if (MORE_WASTE_THAN(tiles_high
->waste
, tiles_low
->waste
))
163 destroy_tiles(tiles_low
);
164 destroy_tiles(tiles_high
);
167 tiles
->waste
= -dim
+ 1;
171 struct tiles
*make_tiles(gint dim
)
173 return make_tiles_rec(dim
, TRUE
);
176 static gboolean
array_pos_empty(struct tiles_iterator
*iter
, gint i
)
178 gint number
= iter
->tiles
->array
[i
];
180 if (iter
->first_pass
)
183 number
+= 1 - (i
% 2);
188 struct tiles_iterator
*tiles_iterator_new(struct tiles
*tiles
)
190 struct tiles_iterator
*iter
= g_new(struct tiles_iterator
, 1);
193 iter
->first_pass
= TRUE
;
194 iter
->current_array_pos
= -1;
195 iter
->remaining_in_pos
= 0;
200 gint
tiles_iterator_next(struct tiles_iterator
* iter
)
202 gint i
, length
= ARRAY_LENGTH
;
204 if (iter
->current_array_pos
>= length
)
207 if (iter
->remaining_in_pos
!= 0) {
208 iter
->remaining_in_pos
--;
209 return 1 << iter
->current_array_pos
;
212 if (iter
->first_pass
) {
213 for (i
= iter
->current_array_pos
+ 1; i
< length
; i
++) {
214 if (!array_pos_empty(iter
, i
)) {
215 iter
->current_array_pos
= i
;
216 iter
->remaining_in_pos
= iter
->tiles
->array
[i
] / 2;
217 if (iter
->tiles
->array
[i
] % 2 == 1)
218 iter
->remaining_in_pos
+= i
% 2;
220 if (iter
->remaining_in_pos
!= 0) {
221 iter
->remaining_in_pos
--;
227 iter
->first_pass
= FALSE
;
228 iter
->current_array_pos
= length
;
231 for (i
= iter
->current_array_pos
- 1; i
>= 0; i
--) {
232 if (!array_pos_empty(iter
, i
)) {
233 iter
->current_array_pos
= i
;
234 iter
->remaining_in_pos
= iter
->tiles
->array
[i
] / 2;
235 if (iter
->tiles
->array
[i
] % 2 == 1)
236 iter
->remaining_in_pos
+= 1 - (i
% 2);
238 if (iter
->remaining_in_pos
!= 0) {
239 iter
->remaining_in_pos
--;
245 iter
->current_array_pos
= length
;
250 static gint
print_tiles(struct tiles
*tiles
)
252 struct tiles_iterator
*iter
= tiles_iterator_new(tiles
);
255 printf("%d tiles:\n", tiles
->nb_tiles
);
256 while ((tile
= tiles_iterator_next(iter
)) > 0)
263 static void test_tile(gint size
)
265 struct tiles
*tiles
= make_tiles(size
);
266 struct tiles_iterator
*iter
;
267 gint count
= 0, tile_size
= 0;
268 gint length
= ARRAY_LENGTH
;
271 for (i
= 0; i
< length
; i
++) {
272 if ((1 << i
) != MAX_TEXTURE_SIZE
&& tiles
->array
[i
] > 1) {
273 printf("Error with %d\n", size
);
277 count
+= tiles
->array
[i
];
278 tile_size
+= tiles
->array
[i
] * 1 << i
;
281 tile_size
-= tiles
->nb_tiles
- 1;
283 if (tile_size
- size
!= tiles
->waste
|| tiles
->nb_tiles
!= count
)
284 printf("Error with %d\n", size
);
286 iter
= tiles_iterator_new(tiles
);
289 while ((i
= tiles_iterator_next(iter
)) > 0) {
294 tile_size
-= tiles
->nb_tiles
- 1;
296 if (tile_size
- size
!= tiles
->waste
|| tiles
->nb_tiles
!= count
)
297 printf("Error with %d\n", size
);
300 destroy_tiles(tiles
);
303 gint
main(gint argc
, char **argv
)
306 struct tiles
*tiles
= make_tiles(atoi(argv
[1]));
307 gint waste
= print_tiles(tiles
);
308 printf("=> waste: %d\n", waste
);
309 destroy_tiles(tiles
);
312 test_tile(g_random_int_range(1, MAX_TEXTURE_SIZE
* 10));