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
|
On process "display" {
set invBilinear $::invBilinear
set rotate $::rotate
# TODO: Do this with a wish, instead of hard-coding the global dict.
dict set ::pipelines "image" [Gpu::pipeline {sampler2D image vec2 imageSize
vec2 pos float radians vec2 scale vec4 crop
fn rotate} {
vec2 a = pos + rotate(scale*-imageSize/2, -radians);
vec2 b = pos + rotate(scale*vec2(imageSize.x, -imageSize.y)/2, -radians);
vec2 c = pos + rotate(scale*imageSize/2, -radians);
vec2 d = pos + rotate(scale*vec2(-imageSize.x, imageSize.y)/2, -radians);
vec2 vertices[4] = vec2[4](a, b, d, c);
return vertices[gl_VertexIndex];
} {fn invBilinear fn rotate} {
vec2 a = pos + rotate(scale*-imageSize/2, -radians);
vec2 b = pos + rotate(scale*vec2(imageSize.x, -imageSize.y)/2, -radians);
vec2 c = pos + rotate(scale*imageSize/2, -radians);
vec2 d = pos + rotate(scale*vec2(-imageSize.x, imageSize.y)/2, -radians);
vec2 p = gl_FragCoord.xy;
vec2 uv = invBilinear(p, a, b, c, d);
if( max( abs(uv.x-0.5), abs(uv.y-0.5))<0.5 ) {
if ((crop[0] < uv.x) && (uv.x < crop[2]) &&
(crop[1] < uv.y) && (uv.y < crop[3])) {
return texture(image, uv);
}
}
return vec4(0.0, 0.0, 0.0, 0.0);
}]
When the GPU has loaded /nfonts/ fonts {
namespace eval ::ImageCache {
# Backing store: stores pairs of (GPU image handle, heap slot version).
variable cache [dict create]
variable CACHE_MAX_SIZE [- $Gpu::ImageManager::GPU_MAX_IMAGES [uplevel {set nfonts}]]
proc getOrInsert {im} {
variable cache
variable CACHE_MAX_SIZE
if {[dict exists $cache $im]} {
lassign [dict get $cache $im] gim cachedVersion
set version [Heap::folkHeapGetVersion [string map {uint8_t void} [::image_t data_ptr $im]]]
if {$version == $cachedVersion} {
# Bump this image to end of cache since it's
# most-recently-accessed.
dict unset cache $im
dict set cache $im [list $gim $cachedVersion]
return $gim
} else {
# This image is stale. Don't retain it.
remove $im
}
}
if {[dict size $cache] >= $CACHE_MAX_SIZE} {
evict
}
if {[dict size $cache] >= $CACHE_MAX_SIZE} {
puts stderr "image: Warning: Out of slots in GPU image cache."
}
set version [Heap::folkHeapGetVersion [string map {uint8_t void} [::image_t data_ptr $im]]]
set gim [Gpu::ImageManager::copyImageToGpu $im]
dict set cache $im [list $gim $version]
return $gim
}
proc evict {} {
variable cache
variable CACHE_MAX_SIZE
set numToEvict [expr {([dict size $cache] + 1) - $CACHE_MAX_SIZE}]
set numEvicted [list]
# Evict stale.
dict for {im v} $cache {
lassign $v gim expectedVersion
set version [Heap::folkHeapGetVersion [string map {uint8_t void} [::image_t data_ptr $im]]]
if {$expectedVersion != $version} {
Gpu::ImageManager::freeGpuImage $gim
lappend numEvicted $im
}
}
foreach im $numEvicted { dict unset cache $im }
# Evict old.
dict for {im v} $cache {
if {$numToEvict - [llength $numEvicted] <= 0} {
break
}
lassign $v gim
Gpu::ImageManager::freeGpuImage $gim
lappend numEvicted $im
}
foreach im $numEvicted { dict unset cache $im }
}
proc remove {im} {
variable cache
if {[dict exists $cache $im]} {
lassign [dict get $cache $im] gim
Gpu::ImageManager::freeGpuImage $gim
}
dict unset cache $im
}
}
}
Wish $::thisProcess receives statements like \
[list /someone/ wishes to draw an image with /...options/]
When /someone/ wishes to draw an image with /...options/ {
if {[dict exists $options center]} {
set center [dict get $options center]
} else {
set center [list [dict get $options x] [dict get $options y]]
}
set im [dict get $options image]
set radians [dict get $options radians]
set scale [dict_getdef $options scale 1.0]
set crop [dict_getdef $options crop [list 0. 0. 1.0 1.0]]
if {[llength $scale] == 1} {
set scale [list $scale $scale]
}
set gim [ImageCache::getOrInsert $im]
Wish the GPU draws pipeline "image" with arguments \
[list $gim [list [image_t width $im] [image_t height $im]] \
$center $radians $scale $crop]
}
}
|