-
-
Notifications
You must be signed in to change notification settings - Fork 179
Expand file tree
/
Copy pathvariable.py
More file actions
466 lines (357 loc) · 16.4 KB
/
variable.py
File metadata and controls
466 lines (357 loc) · 16.4 KB
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# -----------------------------------------------------------------------------
# Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
# Distributed under the (new) BSD License.
# -----------------------------------------------------------------------------
"""
Variables are entry points in the shader that allow to upload CPU data to
the GPU. For OpenGL ES 2.0, there are mainly two types: uniforms and
attributes. The correspondance betwenn GPU and CPU data types is given in the
table below.
=========== ================== == ================== ==============
GLSL Type GLSL/GL Type # GL elementary type Numpy type
=========== ================== == ================== ==============
float gl.GL_FLOAT 1 gl.GL_FLOAT np.float32
vec2 gl.GL_FLOAT_VEC2 2 gl.GL_FLOAT np.float32
vec3 gl.GL_FLOAT_VEC3 3 gl.GL_FLOAT np.float32
vec4 gl.GL_FLOAT_VEC4 4 gl.GL_FLOAT np.float32
int gl.GL_INT 1 gl.GL_INT np.int32
ivec2 gl.GL_INT_VEC2 2 gl.GL_INT np.int32
ivec3 gl.GL_INT_VEC3 3 gl.GL_INT np.int32
ivec4 gl.GL_INT_VEC4 4 gl.GL_INT np.int32
bool gl.GL_BOOL 1 gl.GL_BOOL np.bool_
bvec2 gl.GL_BOOL_VEC2 2 gl.GL_BOOL np.bool_
bvec3 gl.GL_BOOL_VEC3 3 gl.GL_BOOL np.bool_
bvec4 gl.GL_BOOL_VEC4 4 gl.GL_BOOL np.bool_
mat2 gl.GL_FLOAT_MAT2 4 gl.GL_FLOAT np.float32
mat3 gl.GL_FLOAT_MAT3 9 gl.GL_FLOAT np.float32
mat4 gl.GL_FLOAT_MAT4 16 gl.GL_FLOAT np.float32
sampler1D gl.GL_SAMPLER_1D 1 gl.GL_UNSIGNED_INT np.uint32
sampler2D gl.GL_SAMPLER_2D 1 gl.GL_UNSIGNED_INT np.uint32
sampler3D gl.GL_SAMPLER_3D 1 gl.GL_UNSIGNED_INT np.uint32
samplerCube gl.GL_SAMPLER_CUBE 1 gl.GL_UNSIGNED_INT np.uint32
=========== ================== == ================== ==============
.. note::
Most of the time, you don't need to directly manipulate such variables
since they are created automatically when shader code is parsed.
**Example usage**
.. code::
vertex = '''
attribute vec3 position;
void main (void)
{
gl_Position = vec4(position, 1.0);
} '''
fragment = '''
uniform vec4 color;
void main(void)
{
gl_FragColor = color;
} '''
program = gloo.Program(vertex, fragment, count=4)
# program["position"] type is Attribute
# program["color"] type is Uniform
"""
import ctypes
import numpy as np
from glumpy import gl
from glumpy.log import log
from glumpy.gloo.globject import GLObject
from glumpy.gloo.array import VertexArray
from glumpy.gloo.buffer import VertexBuffer
from glumpy.gloo.texture import TextureCube
from glumpy.gloo.texture import Texture1D, Texture2D, Texture3D
from glumpy.gloo.texture import TextureFloat1D, TextureFloat2D, TextureFloat3D
# ------------------------------------------------------------- gl_typeinfo ---
gl_typeinfo = {
gl.GL_FLOAT : ( 1, gl.GL_FLOAT, np.float32),
gl.GL_FLOAT_VEC2 : ( 2, gl.GL_FLOAT, np.float32),
gl.GL_FLOAT_VEC3 : ( 3, gl.GL_FLOAT, np.float32),
gl.GL_FLOAT_VEC4 : ( 4, gl.GL_FLOAT, np.float32),
gl.GL_INT : ( 1, gl.GL_INT, np.int32),
gl.GL_INT_VEC2 : ( 2, gl.GL_INT, np.int32),
gl.GL_INT_VEC3 : ( 3, gl.GL_INT, np.int32),
gl.GL_INT_VEC4 : ( 4, gl.GL_INT, np.int32),
gl.GL_BOOL : ( 1, gl.GL_BOOL, np.bool_),
gl.GL_BOOL_VEC2 : ( 2, gl.GL_BOOL, np.bool_),
gl.GL_BOOL_VEC3 : ( 3, gl.GL_BOOL, np.bool_),
gl.GL_BOOL_VEC4 : ( 4, gl.GL_BOOL, np.bool_),
gl.GL_FLOAT_MAT2 : ( 4, gl.GL_FLOAT, np.float32),
gl.GL_FLOAT_MAT3 : ( 9, gl.GL_FLOAT, np.float32),
gl.GL_FLOAT_MAT4 : (16, gl.GL_FLOAT, np.float32),
gl.GL_SAMPLER_1D : ( 1, gl.GL_UNSIGNED_INT, np.uint32),
gl.GL_SAMPLER_2D : ( 1, gl.GL_UNSIGNED_INT, np.uint32),
gl.GL_SAMPLER_3D : ( 1, gl.GL_UNSIGNED_INT, np.uint32),
gl.GL_SAMPLER_CUBE : ( 1, gl.GL_UNSIGNED_INT, np.uint32)
}
# ---------------------------------------------------------- Variable class ---
class Variable(GLObject):
""" A variable is an interface between a program and data """
def __init__(self, program, name, gtype):
""" Initialize the data into default state """
# Make sure variable type is allowed (for ES 2.0 shader)
if gtype not in [gl.GL_FLOAT, gl.GL_FLOAT_VEC2,
gl.GL_FLOAT_VEC3, gl.GL_FLOAT_VEC4,
gl.GL_INT, gl.GL_BOOL,
gl.GL_FLOAT_MAT2, gl.GL_FLOAT_MAT3,
gl.GL_FLOAT_MAT4, gl.GL_SAMPLER_1D,
gl.GL_SAMPLER_2D, gl.GL_SAMPLER_3D, gl.GL_SAMPLER_CUBE]:
raise TypeError("Unknown variable type")
GLObject.__init__(self)
# Program this variable belongs to
self._program = program
# Name of this variable in the program
self._name = name
# Build dtype
size, _, base = gl_typeinfo[gtype]
self._dtype = (name,base,size)
# GL type
self._gtype = gtype
# CPU data
self._data = None
# Whether this variable is active
self._active = True
@property
def name(self):
""" Variable name """
return self._name
@property
def program(self):
""" Program this variable belongs to """
return self._program
@property
def gtype(self):
""" Type of the underlying variable (as a GL constant) """
return self._gtype
@property
def dtype(self):
""" Equivalent dtype of the variable """
return self._dtype
@property
def active(self):
""" Whether this variable is active in the program """
return self._active
@active.setter
def active(self, active):
""" Whether this variable is active in the program """
self._active = active
@property
def data(self):
""" CPU data """
return self._data
# ----------------------------------------------------------- Uniform class ---
class Uniform(Variable):
""" A Uniform represents a program uniform variable. """
_ufunctions = {
gl.GL_FLOAT: gl.glUniform1fv,
gl.GL_FLOAT_VEC2: gl.glUniform2fv,
gl.GL_FLOAT_VEC3: gl.glUniform3fv,
gl.GL_FLOAT_VEC4: gl.glUniform4fv,
gl.GL_INT: gl.glUniform1iv,
gl.GL_BOOL: gl.glUniform1iv,
gl.GL_FLOAT_MAT2: gl.glUniformMatrix2fv,
gl.GL_FLOAT_MAT3: gl.glUniformMatrix3fv,
gl.GL_FLOAT_MAT4: gl.glUniformMatrix4fv,
gl.GL_SAMPLER_1D: gl.glUniform1i,
gl.GL_SAMPLER_2D: gl.glUniform1i,
gl.GL_SAMPLER_3D: gl.glUniform1i,
gl.GL_SAMPLER_CUBE: gl.glUniform1i
}
def __init__(self, program, name, gtype):
""" Initialize the input into default state """
Variable.__init__(self, program, name, gtype)
size, _, dtype = gl_typeinfo[self._gtype]
self._data = np.zeros(size, dtype)
self._ufunction = Uniform._ufunctions[self._gtype]
self._texture_unit = -1
def set_data(self, data):
""" Assign new data to the variable (deferred operation) """
# Textures need special handling
if self._gtype == gl.GL_SAMPLER_1D:
if isinstance(data, Texture1D):
self._data = data
elif isinstance(self._data, Texture1D):
#self._data.set_data(data)
self._data[...] = data.reshape(self._data.shape)
# Automatic texture creation if required
else:
data = np.array(data,copy=False)
if data.dtype in [np.float16, np.float32, np.float64]:
self._data = data.astype(np.float32).view(Texture1D)
else:
self._data = data.view(Texture1D)
elif self._gtype == gl.GL_SAMPLER_2D:
if isinstance(data, Texture2D):
self._data = data
elif isinstance(self._data, Texture2D):
#self._data.set_data(data)
self._data[...] = data.reshape(self._data.shape)
# Automatic texture creation if required
else:
data = np.array(data,copy=False)
if data.dtype in [np.float16, np.float32, np.float64]:
self._data = data.astype(np.float32).view(Texture2D)
else:
self._data = data.view(Texture2D)
elif self._gtype == gl.GL_SAMPLER_3D:
if isinstance(data, Texture3D):
self._data = data
elif isinstance(self._data, Texture3D):
#self._data.set_data(data)
self._data[...] = data.reshape(self._data.shape)
# Automatic texture creation if required
else:
data = np.array(data,copy=False)
if data.dtype in [np.float16, np.float32, np.float64]:
self._data = data.astype(np.float32).view(Texture3D)
else:
self._data = data.view(Texture3D)
elif self._gtype == gl.GL_SAMPLER_CUBE:
if isinstance(data, TextureCube):
self._data = data
elif isinstance(self._data, TextureCube):
self._data[...] = data.reshape(self._data.shape)
# Automatic texture creation if required
else:
data = np.array(data,copy=False)
if data.dtype in [np.float16, np.float32, np.float64]:
self._data = data.astype(np.float32).view(TextureCube)
else:
self._data = data.view(TextureCube)
else:
self._data[...] = np.array(data,copy=False).ravel()
self._need_update = True
def _activate(self):
if self._gtype in (gl.GL_SAMPLER_1D, gl.GL_SAMPLER_2D, gl.GL_SAMPLER_3D, gl.GL_SAMPLER_CUBE):
if self.data is not None:
log.debug("GPU: Active texture is %d" % self._texture_unit)
gl.glActiveTexture(gl.GL_TEXTURE0 + self._texture_unit)
self.data.activate()
def _update(self):
# Check active status (mandatory)
if not self._active:
raise RuntimeError("Uniform variable is not active")
# WARNING : Uniform are supposed to keep their value between program
# activation/deactivation (from the GL documentation). It has
# been tested on some machines but if it is not the case on
# every machine, we can expect nasty bugs from this early
# return
# Matrices (need a transpose argument)
if self._gtype in (gl.GL_FLOAT_MAT2, gl.GL_FLOAT_MAT3, gl.GL_FLOAT_MAT4):
# OpenGL ES 2.0 does not support transpose
transpose = False
self._ufunction(self._handle, 1, transpose, self._data)
# Textures (need to get texture count)
elif self._gtype in (gl.GL_SAMPLER_1D, gl.GL_SAMPLER_2D, gl.GL_SAMPLER_3D, gl.GL_SAMPLER_CUBE):
# texture = self.data
log.debug("GPU: Activactin texture %d" % self._texture_unit)
# gl.glActiveTexture(gl.GL_TEXTURE0 + self._unit)
# gl.glBindTexture(texture.target, texture.handle)
gl.glUniform1i(self._handle, self._texture_unit)
# Regular uniform
else:
self._ufunction(self._handle, 1, self._data)
def _create(self):
""" Create uniform on GPU (get handle) """
self._handle = gl.glGetUniformLocation(self._program.handle, self._name)
# --------------------------------------------------------- Attribute class ---
class Attribute(Variable):
""" An Attribute represents a program attribute variable """
_afunctions = {
gl.GL_FLOAT: gl.glVertexAttrib1f,
gl.GL_FLOAT_VEC2: gl.glVertexAttrib2f,
gl.GL_FLOAT_VEC3: gl.glVertexAttrib3f,
gl.GL_FLOAT_VEC4: gl.glVertexAttrib4f
}
def __init__(self, program, name, gtype):
""" Initialize the input into default state """
Variable.__init__(self, program, name, gtype)
# Number of elements this attribute links to (in the attached buffer)
self._size = 0
# Whether this attribure is generic
self._generic = False
def set_data(self, data):
""" Assign new data to the variable (deferred operation) """
isnumeric = isinstance(data, (float, int))
# New vertex buffer
if isinstance(data, (VertexBuffer,VertexArray)):
self._data = data
# We already have a vertex buffer
elif isinstance(self._data, (VertexBuffer,VertexArray)):
self._data[...] = data
# Data is a tuple with size <= 4, we assume this designates a generate
# vertex attribute.
elif (isnumeric or (isinstance(data, (tuple, list)) and
len(data) in (1, 2, 3, 4) and
isinstance(data[0], (float, int)))):
# Let numpy convert the data for us
_, _, dtype = gl_typeinfo[self._gtype]
self._data = np.array(data).astype(dtype)
self._generic = True
self._need_update = True
self._afunction = Attribute._afunctions[self._gtype]
return
# For array-like, we need to build a proper VertexBuffer to be able to
# upload it later to GPU memory.
else: #lif not isinstance(data, VertexBuffer):
name,base,count = self.dtype
data = np.array(data,dtype=base,copy=False)
data = data.ravel().view([self.dtype])
# WARNING : transform data with the right type
# data = np.array(data,copy=False)
self._data = data.view(VertexBuffer)
self._generic = False
def _activate(self):
if isinstance(self.data, (VertexBuffer, VertexArray)):
self.data.activate()
size, gtype, dtype = gl_typeinfo[self._gtype]
stride = self.data.stride
offset = ctypes.c_void_p(self.data.offset)
gl.glEnableVertexAttribArray(self.handle)
gl.glVertexAttribPointer(self.handle, size, gtype, gl.GL_FALSE, stride, offset)
def _deactivate(self):
if isinstance(self.data,VertexBuffer):
self.data.deactivate()
if self.handle >= 0:
gl.glDisableVertexAttribArray(self.handle)
elif isinstance(self.data,VertexArray):
self.data.deactivate()
def _update(self):
""" Actual upload of data to GPU memory """
log.debug("GPU: Updating %s" % self.name)
# Check active status (mandatory)
# if not self._active:
# raise RuntimeError("Attribute variable is not active")
# if self._data is None:
# raise RuntimeError("Attribute variable data is not set")
# Generic vertex attribute (all vertices receive the same value)
if self._generic:
if self._handle >= 0:
gl.glDisableVertexAttribArray(self._handle)
self._afunction(self._handle, *self._data)
# Regular vertex buffer
elif self.handle >= 0:
#if self._need_update:
# self.data._update()
# self._need_update = False
# Get relevant information from gl_typeinfo
size, gtype, dtype = gl_typeinfo[self._gtype]
stride = self.data.stride
# Make offset a pointer, or it will be interpreted as a small array
offset = ctypes.c_void_p(self.data.offset)
gl.glEnableVertexAttribArray(self.handle)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.data.handle)
gl.glVertexAttribPointer(self.handle, size, gtype, gl.GL_FALSE, stride, offset)
def _create(self):
""" Create attribute on GPU (get handle) """
self._handle = gl.glGetAttribLocation(self._program.handle, self.name)
@property
def size(self):
""" Size of the underlying vertex buffer """
if self._data is None:
return 0
return self._data.size
def __len__(self):
""" Length of the underlying vertex buffer """
if self._data is None:
return 0
return len(self._data)