changeset 7297 | af64b509725c |
parent 7295 | e70b81854fb9 |
child 7301 | bea42438a2ec |
7295:e70b81854fb9 | 7297:af64b509725c |
---|---|
9 |
9 |
10 procedure initModule; |
10 procedure initModule; |
11 |
11 |
12 function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; |
12 function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; |
13 procedure FreeTexture_(sprite: PTexture); |
13 procedure FreeTexture_(sprite: PTexture); |
14 procedure DebugAtlas; |
|
14 |
15 |
15 implementation |
16 implementation |
16 |
17 |
17 uses GLunit, uBinPacker, uDebug, png, sysutils; |
18 uses GLunit, uBinPacker, uDebug, png, sysutils, uTextures; |
18 |
19 |
19 const |
20 const |
20 MaxAtlases = 1; // Maximum number of atlases (textures) to allocate |
21 MaxAtlases = 4; // Maximum number of atlases (textures) to allocate |
21 MaxTexSize = 4096; // Maximum atlas size in pixels |
22 MaxTexSize = 1024; // Maximum atlas size in pixels |
22 MinTexSize = 128; // Minimum atlas size in pixels |
23 MinTexSize = 128; // Minimum atlas size in pixels |
23 CompressionThreshold = 0.4; // Try to compact (half the size of) an atlas, when occupancy is less than this |
24 CompressionThreshold = 0.4; // Try to compact (half the size of) an atlas, when occupancy is less than this |
24 |
25 |
25 type |
26 type |
26 AtlasInfo = record |
27 AtlasInfo = record |
33 Info: array[0..MaxAtlases-1] of AtlasInfo; |
34 Info: array[0..MaxAtlases-1] of AtlasInfo; |
34 |
35 |
35 |
36 |
36 //////////////////////////////////////////////////////////////////////////////// |
37 //////////////////////////////////////////////////////////////////////////////// |
37 // Debug routines |
38 // Debug routines |
39 |
|
40 procedure AssertCount(tex: PTexture; count: Integer); |
|
41 var |
|
42 i, j: Integer; |
|
43 found: Integer; |
|
44 begin |
|
45 found:= 0; |
|
46 for i:= 0 to pred(MaxAtlases) do |
|
47 begin |
|
48 if not Info[i].Allocated then |
|
49 continue; |
|
50 for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do |
|
51 begin |
|
52 if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then |
|
53 inc(found); |
|
54 end; |
|
55 end; |
|
56 if found <> count then |
|
57 begin |
|
58 writeln('AssertCount(', IntToHex(Integer(tex), 8), ') failed, found ', found, ' times'); |
|
59 |
|
60 for i:= 0 to pred(MaxAtlases) do |
|
61 begin |
|
62 if not Info[i].Allocated then |
|
63 continue; |
|
64 for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do |
|
65 begin |
|
66 if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then |
|
67 writeln(' found in atlas ', i, ' at slot ', j); |
|
68 end; |
|
69 end; |
|
70 halt(-2); |
|
71 end; |
|
72 end; |
|
38 |
73 |
39 var |
74 var |
40 DumpID: Integer; |
75 DumpID: Integer; |
41 DumpFile: File of byte; |
76 DumpFile: File of byte; |
42 |
77 |
71 s:= IntToStr(i); |
106 s:= IntToStr(i); |
72 if (i < 10) then s:='0' + s; |
107 if (i < 10) then s:='0' + s; |
73 if (i < 100) then s:='0' + s; |
108 if (i < 100) then s:='0' + s; |
74 |
109 |
75 IntToStrPad:=s; |
110 IntToStrPad:=s; |
111 end; |
|
112 |
|
113 // GL1 ATLAS DEBUG ONLY CODE! |
|
114 procedure DebugAtlas; |
|
115 var |
|
116 vp: array[0..3] of GLint; |
|
117 prog: GLint; |
|
118 i: Integer; |
|
119 x, y: Integer; |
|
120 const |
|
121 SZ = 512; |
|
122 begin |
|
123 x:= 0; |
|
124 y:= 0; |
|
125 for i:= 0 to pred(MaxAtlases) do |
|
126 begin |
|
127 if not Info[i].allocated then |
|
128 continue; |
|
129 glGetIntegerv(GL_VIEWPORT, @vp); |
|
130 glGetIntegerv(GL_CURRENT_PROGRAM, @prog); |
|
131 |
|
132 glUseProgram(0); |
|
133 glPushMatrix; |
|
134 glLoadIdentity; |
|
135 glOrtho(0, vp[2], vp[3], 0, -1, 1); |
|
136 |
|
137 |
|
138 glBindTexture(GL_TEXTURE_2D, Info[i].TextureInfo.id); |
|
139 glBegin(GL_QUADS); |
|
140 glTexCoord2f(0.0, 0.0); |
|
141 glVertex2i(x * SZ, y * SZ); |
|
142 glTexCoord2f(1.0, 0.0); |
|
143 glVertex2i((x + 1) * SZ, y * SZ); |
|
144 glTexCoord2f(1.0, 1.0); |
|
145 glVertex2i((x + 1) * SZ, (y + 1) * SZ); |
|
146 glTexCoord2f(0.0, 1.0); |
|
147 glVertex2i(x * SZ, (y + 1) * SZ); |
|
148 glEnd(); |
|
149 |
|
150 glPopMatrix; |
|
151 |
|
152 inc(x); |
|
153 if (x = 2) then |
|
154 begin |
|
155 x:=0; |
|
156 inc(y); |
|
157 end; |
|
158 |
|
159 |
|
160 glUseProgram(prog); |
|
161 end; |
|
76 end; |
162 end; |
77 |
163 |
78 procedure DumpAtlas(var info: AtlasInfo); |
164 procedure DumpAtlas(var info: AtlasInfo); |
79 var |
165 var |
80 png: png_structp; |
166 png: png_structp; |
144 createTexture.w:= width; |
230 createTexture.w:= width; |
145 createTexture.h:= height; |
231 createTexture.h:= height; |
146 createTexture.priority:= 0; |
232 createTexture.priority:= 0; |
147 glGenTextures(1, @createTexture.id); |
233 glGenTextures(1, @createTexture.id); |
148 glBindTexture(GL_TEXTURE_2D, createTexture.id); |
234 glBindTexture(GL_TEXTURE_2D, createTexture.id); |
235 |
|
236 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
|
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
149 |
238 |
150 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); |
239 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); |
151 |
240 |
152 GetMem(NullTex, width * height * 4); |
241 GetMem(NullTex, width * height * 4); |
153 FillChar(NullTex^, width * height * 4, 0); |
242 FillChar(NullTex^, width * height * 4, 0); |
273 procedure Upload(var info: AtlasInfo; sprite: Rectangle; surf: PSDL_Surface); |
362 procedure Upload(var info: AtlasInfo; sprite: Rectangle; surf: PSDL_Surface); |
274 var |
363 var |
275 sp: PTexture; |
364 sp: PTexture; |
276 i, j, stride: Integer; |
365 i, j, stride: Integer; |
277 scanline: PByte; |
366 scanline: PByte; |
367 r: TSDL_Rect; |
|
278 begin |
368 begin |
279 writeln('Uploading sprite to ', sprite.x, ',', sprite.y, ',', sprite.width, ',', sprite.height); |
369 writeln('Uploading sprite to ', sprite.x, ',', sprite.y, ',', sprite.width, ',', sprite.height); |
280 sp:= PTexture(sprite.UserData); |
370 sp:= PTexture(sprite.UserData); |
281 sp^.x:= sprite.x; |
371 sp^.x:= sprite.x; |
282 sp^.y:= sprite.y; |
372 sp^.y:= sprite.y; |
286 if SDL_MustLock(surf) then |
376 if SDL_MustLock(surf) then |
287 SDLTry(SDL_LockSurface(surf) >= 0, true); |
377 SDLTry(SDL_LockSurface(surf) >= 0, true); |
288 |
378 |
289 //if GrayScale then |
379 //if GrayScale then |
290 // Surface2GrayScale(surf); |
380 // Surface2GrayScale(surf); |
291 DebugColorize(surf); |
381 //DebugColorize(surf); |
292 |
382 |
293 glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id); |
383 glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id); |
294 if (sp^.isRotated) then |
384 if (sp^.isRotated) then |
295 begin |
385 begin |
296 scanline:= surf^.pixels; |
386 scanline:= surf^.pixels; |
304 glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x, sprite.y, sprite.width, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels); |
394 glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x, sprite.y, sprite.width, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels); |
305 glBindTexture(GL_TEXTURE_2D, 0); |
395 glBindTexture(GL_TEXTURE_2D, 0); |
306 |
396 |
307 if SDL_MustLock(surf) then |
397 if SDL_MustLock(surf) then |
308 SDL_UnlockSurface(surf); |
398 SDL_UnlockSurface(surf); |
309 end; |
399 |
310 |
400 r.x:= 0; |
311 procedure Repack(var info: AtlasInfo; newAtlas: Atlas; newSprite: PTexture; surf: PSDL_Surface); |
401 r.y:= 0; |
402 r.w:= sp^.w; |
|
403 r.h:= sp^.h; |
|
404 ComputeTexcoords(sp, @r, @sp^.tb); |
|
405 end; |
|
406 |
|
407 procedure Repack(var info: AtlasInfo; newAtlas: Atlas); |
|
312 var |
408 var |
313 base: PByte; |
409 base: PByte; |
314 oldSize: Integer; |
410 oldSize: Integer; |
315 oldWidth: Integer; |
411 oldWidth: Integer; |
316 offset: Integer; |
412 offset: Integer; |
371 end; |
467 end; |
372 |
468 |
373 //////////////////////////////////////////////////////////////////////////////// |
469 //////////////////////////////////////////////////////////////////////////////// |
374 // Sprite allocation logic |
470 // Sprite allocation logic |
375 |
471 |
376 function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; |
472 function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; newSprite: Size): boolean; |
377 newSprite: Size; surf: PSDL_Surface): boolean; |
|
378 var |
473 var |
379 sizes: SizeList; |
474 sizes: SizeList; |
380 repackedAtlas: Atlas; |
475 repackedAtlas: Atlas; |
381 sprite: PTexture; |
476 sprite: PTexture; |
382 i: Integer; |
477 i: Integer; |
400 repackedAtlas:= atlasNew(w, h); |
495 repackedAtlas:= atlasNew(w, h); |
401 rectangleListInit(rects); |
496 rectangleListInit(rects); |
402 if atlasInsertSet(repackedAtlas, sizes, rects) then |
497 if atlasInsertSet(repackedAtlas, sizes, rects) then |
403 begin |
498 begin |
404 TryRepack:= true; |
499 TryRepack:= true; |
405 if hasNewSprite then |
500 Repack(info, repackedAtlas); |
406 sprite:= PTexture(newSprite.UserData) |
|
407 else |
|
408 sprite:= nil; |
|
409 Repack(info, repackedAtlas, sprite, surf); |
|
410 // repack assigns repackedAtlas to the current info and deletes the old one |
501 // repack assigns repackedAtlas to the current info and deletes the old one |
411 // thus we wont do atlasDelete(repackedAtlas); here |
502 // thus we wont do atlasDelete(repackedAtlas); here |
412 rectangleListClear(rects); |
503 rectangleListClear(rects); |
413 sizeListClear(sizes); |
504 sizeListClear(sizes); |
414 DumpAtlas(info); |
505 //DumpAtlas(info); |
415 exit; |
506 exit; |
416 end; |
507 end; |
417 |
508 |
418 rectangleListClear(rects); |
509 rectangleListClear(rects); |
419 sizeListClear(sizes); |
510 sizeListClear(sizes); |
429 |
520 |
430 if atlasInsertAdaptive(info.PackerInfo, newSprite, rect) then |
521 if atlasInsertAdaptive(info.PackerInfo, newSprite, rect) then |
431 begin |
522 begin |
432 // we succeeded adaptivley allocating the sprite to the i'th atlas. |
523 // we succeeded adaptivley allocating the sprite to the i'th atlas. |
433 Upload(info, rect, surf); |
524 Upload(info, rect, surf); |
434 DumpAtlas(info); |
525 //DumpAtlas(info); |
435 TryInsert:= true; |
526 TryInsert:= true; |
436 end; |
527 end; |
437 end; |
528 end; |
438 |
529 |
439 function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; |
530 function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; |
458 sprite^.h:= surf^.h; |
549 sprite^.h:= surf^.h; |
459 sprite^.x:= 0; |
550 sprite^.x:= 0; |
460 sprite^.y:= 0; |
551 sprite^.y:= 0; |
461 sprite^.isRotated:= false; |
552 sprite^.isRotated:= false; |
462 sprite^.surface:= surf; |
553 sprite^.surface:= surf; |
554 sprite^.shared:= true; |
|
463 |
555 |
464 sz:= SizeForSprite(sprite); |
556 sz:= SizeForSprite(sprite); |
465 |
557 |
466 // STEP 1 |
558 // STEP 1 |
467 // try to allocate the new sprite in one of the existing atlases |
559 // try to allocate the new sprite in one of the existing atlases |
468 for i:= 0 to pred(MaxAtlases) do |
560 for i:= 0 to pred(MaxAtlases) do |
469 begin |
561 begin |
470 if not Info[i].Allocated then |
562 if not Info[i].Allocated then |
471 continue; |
563 continue; |
472 if TryInsert(Info[i], sz, surf) then |
564 if TryInsert(Info[i], sz, surf) then |
473 exit; |
565 exit; |
474 end; |
566 end; |
475 |
567 |
476 |
568 |
477 // STEP 2 |
569 // STEP 2 |
478 // none of the atlases has space left for the allocation, try a garbage collection |
570 // none of the atlases has space left for the allocation, try a garbage collection |
479 for i:= 0 to pred(MaxAtlases) do |
571 for i:= 0 to pred(MaxAtlases) do |
480 begin |
572 begin |
481 if not Info[i].Allocated then |
573 if not Info[i].Allocated then |
482 continue; |
574 continue; |
483 |
575 |
484 if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz, surf) then |
576 if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz) then |
485 exit; |
577 exit; |
486 end; |
578 end; |
487 |
579 |
488 // STEP 3 |
580 // STEP 3 |
489 // none of the atlases could be repacked in a way to fit the new sprite, try enlarging |
581 // none of the atlases could be repacked in a way to fit the new sprite, try enlarging |
496 currentHeight:= Info[i].PackerInfo.height; |
588 currentHeight:= Info[i].PackerInfo.height; |
497 |
589 |
498 EnlargeSize(currentWidth, currentHeight); |
590 EnlargeSize(currentWidth, currentHeight); |
499 while (currentWidth <= MaxTexSize) and (currentHeight <= MaxTexSize) do |
591 while (currentWidth <= MaxTexSize) and (currentHeight <= MaxTexSize) do |
500 begin |
592 begin |
501 if TryRepack(Info[i], currentWidth, currentHeight, true, sz, surf) then |
593 if TryRepack(Info[i], currentWidth, currentHeight, true, sz) then |
502 exit; |
594 exit; |
503 EnlargeSize(currentWidth, currentHeight); |
595 EnlargeSize(currentWidth, currentHeight); |
504 end; |
596 end; |
505 end; |
597 end; |
506 |
598 |
553 unused: Size; |
645 unused: Size; |
554 begin |
646 begin |
555 if sprite = nil then |
647 if sprite = nil then |
556 exit; |
648 exit; |
557 |
649 |
650 deleteAt:= -1; |
|
558 for i:= 0 to pred(MaxAtlases) do |
651 for i:= 0 to pred(MaxAtlases) do |
559 begin |
652 begin |
560 if sprite^.atlas <> @Info[i].TextureInfo then |
653 if sprite^.atlas <> @Info[i].TextureInfo then |
561 continue; |
654 continue; |
562 |
655 |
568 deleteAt:= j |
661 deleteAt:= j |
569 else |
662 else |
570 inc(usedArea, r.width * r.height); |
663 inc(usedArea, r.width * r.height); |
571 end; |
664 end; |
572 |
665 |
573 rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, j); |
666 rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, deleteAt); |
574 dispose(sprite); |
667 dispose(sprite); |
575 |
668 |
576 while true do |
669 while true do |
577 begin |
670 begin |
578 atlasW:= Info[i].PackerInfo.width; |
671 atlasW:= Info[i].PackerInfo.width; |
584 if (atlasW = MinTexSize) and (atlasH = MinTexSize) then |
677 if (atlasW = MinTexSize) and (atlasH = MinTexSize) then |
585 exit; // we could try to move everything from this to another atlas here |
678 exit; // we could try to move everything from this to another atlas here |
586 |
679 |
587 CompactSize(atlasW, atlasH); |
680 CompactSize(atlasW, atlasH); |
588 unused:= unused; |
681 unused:= unused; |
589 TryRepack(Info[i], atlasW, atlasH, false, unused, nil); |
682 TryRepack(Info[i], atlasW, atlasH, false, unused); |
590 end; |
683 end; |
684 |
|
685 exit; |
|
591 end; |
686 end; |
592 end; |
687 end; |
593 |
688 |
594 procedure initModule; |
689 procedure initModule; |
595 var |
690 var |