pyglet 2.1.13__py3-none-any.whl → 3.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +5 -21
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +28 -8
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +154 -194
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.13.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
pyglet/graphics/shader.py CHANGED
@@ -1,1341 +1,108 @@
1
1
  from __future__ import annotations
2
2
 
3
- import re
4
- import warnings
3
+ import abc
4
+ import ctypes
5
5
  import weakref
6
- from _ctypes import _Pointer, _SimpleCData
7
- from collections import defaultdict
8
- from ctypes import (
9
- POINTER,
10
- Array,
11
- Structure,
12
- addressof,
13
- byref,
14
- c_buffer,
15
- c_byte,
16
- c_char,
17
- c_char_p,
18
- c_double,
19
- c_float,
20
- c_int,
21
- c_short,
22
- c_ubyte,
23
- c_uint,
24
- c_ushort,
25
- cast,
26
- create_string_buffer,
27
- pointer,
28
- sizeof,
29
- string_at,
30
- )
31
- from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence, Type, Union
32
-
33
- import pyglet
34
- from pyglet.gl import Context, GLException, gl, gl_info
35
- from pyglet.gl.gl import (
36
- GL_ARRAY_BUFFER,
37
- GL_FALSE,
38
- GL_INFO_LOG_LENGTH,
39
- GL_LINK_STATUS,
40
- GL_MAP_READ_BIT,
41
- GL_TRUE,
42
- GL_UNIFORM_BUFFER,
43
- glAttachShader,
44
- glBindBuffer,
45
- glBindBufferBase,
46
- glCreateProgram,
47
- glDeleteProgram,
48
- glDeleteShader,
49
- glDetachShader,
50
- glDispatchCompute,
51
- glEnableVertexAttribArray,
52
- glGetActiveAttrib,
53
- glGetProgramInfoLog,
54
- glGetProgramiv,
55
- glLinkProgram,
56
- glMapBufferRange,
57
- glMemoryBarrier,
58
- glUnmapBuffer,
59
- glUseProgram,
60
- glVertexAttribDivisor,
61
- glVertexAttribIPointer,
62
- glVertexAttribPointer,
63
- )
64
- from pyglet.graphics.vertexbuffer import AttributeBufferObject, BufferObject
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass
8
+ from typing import Literal, Sequence, Any, TYPE_CHECKING, Callable, Protocol
65
9
 
66
10
  if TYPE_CHECKING:
11
+ from pyglet.customtypes import DataTypes, CType
12
+ from pyglet.graphics.vertexdomain import IndexedVertexList, VertexList, InstanceIndexedVertexList, InstanceVertexList
13
+ from pyglet.graphics import GeometryMode, Batch, Group
67
14
  from _weakref import CallableProxyType
68
15
 
69
- from pyglet.graphics import Batch, Group
70
- from pyglet.graphics.vertexdomain import IndexedVertexList, VertexList
71
16
 
72
- _debug_gl_shaders = pyglet.options['debug_gl_shaders']
73
-
74
-
75
- class ShaderException(BaseException): # noqa: D101
17
+ class ShaderException(BaseException):
76
18
  pass
77
19
 
78
-
79
- CTypesDataType = Type[_SimpleCData]
80
- CTypesPointer = _Pointer
81
20
  ShaderType = Literal['vertex', 'fragment', 'geometry', 'compute', 'tesscontrol', 'tessevaluation']
82
- GLDataType = Union[Type[gl.GLint], Type[gl.GLfloat], Type[gl.GLboolean], int]
83
- GLFunc = Callable
84
-
85
- _c_types: dict[int, CTypesDataType] = {
86
- gl.GL_BYTE: c_byte,
87
- gl.GL_UNSIGNED_BYTE: c_ubyte,
88
- gl.GL_SHORT: c_short,
89
- gl.GL_UNSIGNED_SHORT: c_ushort,
90
- gl.GL_INT: c_int,
91
- gl.GL_UNSIGNED_INT: c_uint,
92
- gl.GL_FLOAT: c_float,
93
- gl.GL_DOUBLE: c_double,
94
- }
95
-
96
- _shader_types: dict[ShaderType, int] = {
97
- 'compute': gl.GL_COMPUTE_SHADER,
98
- 'fragment': gl.GL_FRAGMENT_SHADER,
99
- 'geometry': gl.GL_GEOMETRY_SHADER,
100
- 'tesscontrol': gl.GL_TESS_CONTROL_SHADER,
101
- 'tessevaluation': gl.GL_TESS_EVALUATION_SHADER,
102
- 'vertex': gl.GL_VERTEX_SHADER,
103
- }
104
-
105
- _uniform_getters: dict[GLDataType, Callable] = {
106
- gl.GLint: gl.glGetUniformiv,
107
- gl.GLfloat: gl.glGetUniformfv,
108
- gl.GLboolean: gl.glGetUniformiv,
109
- }
110
-
111
- _uniform_setters: dict[int, tuple[GLDataType, GLFunc, GLFunc, int]] = {
112
- # uniform: gl_type, legacy_setter, setter, length
113
- gl.GL_BOOL: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
114
- gl.GL_BOOL_VEC2: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 2),
115
- gl.GL_BOOL_VEC3: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 3),
116
- gl.GL_BOOL_VEC4: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 4),
117
-
118
- gl.GL_INT: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
119
- gl.GL_INT_VEC2: (gl.GLint, gl.glUniform2iv, gl.glProgramUniform2iv, 2),
120
- gl.GL_INT_VEC3: (gl.GLint, gl.glUniform3iv, gl.glProgramUniform3iv, 3),
121
- gl.GL_INT_VEC4: (gl.GLint, gl.glUniform4iv, gl.glProgramUniform4iv, 4),
122
-
123
- gl.GL_UNSIGNED_INT: (gl.GLuint, gl.glUniform1uiv, gl.glProgramUniform1uiv, 1),
124
- gl.GL_UNSIGNED_INT_VEC2: (gl.GLuint, gl.glUniform2uiv, gl.glProgramUniform2uiv, 2),
125
- gl.GL_UNSIGNED_INT_VEC3: (gl.GLuint, gl.glUniform3uiv, gl.glProgramUniform3uiv, 3),
126
- gl.GL_UNSIGNED_INT_VEC4: (gl.GLuint, gl.glUniform4uiv, gl.glProgramUniform4uiv, 4),
127
-
128
- gl.GL_FLOAT: (gl.GLfloat, gl.glUniform1fv, gl.glProgramUniform1fv, 1),
129
- gl.GL_FLOAT_VEC2: (gl.GLfloat, gl.glUniform2fv, gl.glProgramUniform2fv, 2),
130
- gl.GL_FLOAT_VEC3: (gl.GLfloat, gl.glUniform3fv, gl.glProgramUniform3fv, 3),
131
- gl.GL_FLOAT_VEC4: (gl.GLfloat, gl.glUniform4fv, gl.glProgramUniform4fv, 4),
132
-
133
- # 1D Samplers
134
- gl.GL_SAMPLER_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
135
- gl.GL_SAMPLER_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
136
- gl.GL_INT_SAMPLER_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
137
- gl.GL_INT_SAMPLER_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
138
- gl.GL_UNSIGNED_INT_SAMPLER_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
139
- gl.GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
140
-
141
- # 2D Samplers
142
- gl.GL_SAMPLER_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
143
- gl.GL_SAMPLER_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
144
- gl.GL_INT_SAMPLER_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
145
- gl.GL_INT_SAMPLER_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
146
- gl.GL_UNSIGNED_INT_SAMPLER_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
147
- gl.GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
148
- # Multisample
149
- gl.GL_SAMPLER_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
150
- gl.GL_INT_SAMPLER_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
151
- gl.GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
152
-
153
- # Cube Samplers
154
- gl.GL_SAMPLER_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
155
- gl.GL_INT_SAMPLER_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
156
- gl.GL_UNSIGNED_INT_SAMPLER_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
157
- gl.GL_SAMPLER_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
158
- gl.GL_INT_SAMPLER_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
159
- gl.GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
160
-
161
- # 3D Samplers
162
- gl.GL_SAMPLER_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
163
- gl.GL_INT_SAMPLER_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
164
- gl.GL_UNSIGNED_INT_SAMPLER_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
165
-
166
- gl.GL_FLOAT_MAT2: (gl.GLfloat, gl.glUniformMatrix2fv, gl.glProgramUniformMatrix2fv, 4),
167
- gl.GL_FLOAT_MAT3: (gl.GLfloat, gl.glUniformMatrix3fv, gl.glProgramUniformMatrix3fv, 9),
168
- gl.GL_FLOAT_MAT4: (gl.GLfloat, gl.glUniformMatrix4fv, gl.glProgramUniformMatrix4fv, 16),
169
-
170
- # TODO: test/implement these:
171
- # GL_FLOAT_MAT2x3: glUniformMatrix2x3fv, glProgramUniformMatrix2x3fv,
172
- # GL_FLOAT_MAT2x4: glUniformMatrix2x4fv, glProgramUniformMatrix2x4fv,
173
- # GL_FLOAT_MAT3x2: glUniformMatrix3x2fv, glProgramUniformMatrix3x2fv,
174
- # GL_FLOAT_MAT3x4: glUniformMatrix3x4fv, glProgramUniformMatrix3x4fv,
175
- # GL_FLOAT_MAT4x2: glUniformMatrix4x2fv, glProgramUniformMatrix4x2fv,
176
- # GL_FLOAT_MAT4x3: glUniformMatrix4x3fv, glProgramUniformMatrix4x3fv,
177
-
178
- gl.GL_IMAGE_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
179
- gl.GL_IMAGE_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
180
- gl.GL_IMAGE_2D_RECT: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
181
- gl.GL_IMAGE_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
182
- gl.GL_IMAGE_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
183
- gl.GL_IMAGE_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
184
- gl.GL_IMAGE_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
185
- gl.GL_IMAGE_2D_MULTISAMPLE_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
186
- gl.GL_IMAGE_BUFFER: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
187
- gl.GL_IMAGE_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
188
- gl.GL_IMAGE_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
189
-
190
- gl.GL_INT_IMAGE_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
191
- gl.GL_INT_IMAGE_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
192
- gl.GL_INT_IMAGE_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
193
- gl.GL_INT_IMAGE_2D_RECT: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
194
- gl.GL_INT_IMAGE_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
195
- gl.GL_INT_IMAGE_BUFFER: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
196
- gl.GL_INT_IMAGE_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
197
- gl.GL_INT_IMAGE_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
198
- gl.GL_INT_IMAGE_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
199
- gl.GL_INT_IMAGE_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
200
- gl.GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
201
-
202
- gl.GL_UNSIGNED_INT_IMAGE_1D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
203
- gl.GL_UNSIGNED_INT_IMAGE_2D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
204
- gl.GL_UNSIGNED_INT_IMAGE_3D: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
205
- gl.GL_UNSIGNED_INT_IMAGE_2D_RECT: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
206
- gl.GL_UNSIGNED_INT_IMAGE_CUBE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
207
- gl.GL_UNSIGNED_INT_IMAGE_BUFFER: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
208
- gl.GL_UNSIGNED_INT_IMAGE_1D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
209
- gl.GL_UNSIGNED_INT_IMAGE_2D_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
210
- gl.GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
211
- gl.GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
212
- gl.GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: (gl.GLint, gl.glUniform1iv, gl.glProgramUniform1iv, 1),
213
- }
214
-
215
- _attribute_types: dict[int, tuple[int, str]] = {
216
- gl.GL_BOOL: (1, '?'),
217
- gl.GL_BOOL_VEC2: (2, '?'),
218
- gl.GL_BOOL_VEC3: (3, '?'),
219
- gl.GL_BOOL_VEC4: (4, '?'),
220
-
221
- gl.GL_INT: (1, 'i'),
222
- gl.GL_INT_VEC2: (2, 'i'),
223
- gl.GL_INT_VEC3: (3, 'i'),
224
- gl.GL_INT_VEC4: (4, 'i'),
225
-
226
- gl.GL_UNSIGNED_INT: (1, 'I'),
227
- gl.GL_UNSIGNED_INT_VEC2: (2, 'I'),
228
- gl.GL_UNSIGNED_INT_VEC3: (3, 'I'),
229
- gl.GL_UNSIGNED_INT_VEC4: (4, 'I'),
230
-
231
- gl.GL_FLOAT: (1, 'f'),
232
- gl.GL_FLOAT_VEC2: (2, 'f'),
233
- gl.GL_FLOAT_VEC3: (3, 'f'),
234
- gl.GL_FLOAT_VEC4: (4, 'f'),
235
-
236
- gl.GL_DOUBLE: (1, 'd'),
237
- gl.GL_DOUBLE_VEC2: (2, 'd'),
238
- gl.GL_DOUBLE_VEC3: (3, 'd'),
239
- gl.GL_DOUBLE_VEC4: (4, 'd'),
240
- }
241
-
242
-
243
- # Accessor classes:
244
-
245
- class Attribute:
246
- """Abstract accessor for an attribute in a mapped buffer."""
247
- stride: int
248
- element_size: int
249
- c_type: CTypesDataType
250
- instance: bool
251
- normalize: bool
252
- gl_type: int
253
- count: int
254
- location: int
255
- name: str
256
-
257
- def __init__(self, name: str, location: int, count: int, gl_type: int, normalize: bool, instance: bool) -> None:
258
- """Create the attribute accessor.
259
-
260
- Args:
261
- name:
262
- Name of the vertex attribute.
263
- location:
264
- Location (index) of the vertex attribute.
265
- count:
266
- Number of components in the attribute.
267
- gl_type:
268
- OpenGL type enumerant; for example, ``GL_FLOAT``
269
- normalize:
270
- True if OpenGL should normalize the values
271
- instance:
272
- True if OpenGL should treat this as an instanced attribute.
273
-
274
- """
275
- self.name = name
276
- self.location = location
277
- self.count = count
278
- self.gl_type = gl_type
279
- self.normalize = normalize
280
- self.instance = instance
281
-
282
- self.c_type = _c_types[gl_type]
283
-
284
- self.element_size = sizeof(self.c_type)
285
- self.stride = count * self.element_size
286
-
287
- self._is_int = gl_type in (gl.GL_INT, gl.GL_SHORT, gl.GL_BYTE, gl.GL_UNSIGNED_INT,
288
- gl.GL_UNSIGNED_SHORT, gl.GL_UNSIGNED_BYTE) and self.normalize is False
289
-
290
- def enable(self) -> None:
291
- """Enable the attribute."""
292
- glEnableVertexAttribArray(self.location)
293
-
294
- def set_pointer(self, ptr: int) -> None:
295
- """Setup this attribute to point to the currently bound buffer at the given offset.
296
-
297
- ``offset`` should be based on the currently bound buffer's ``ptr`` member.
298
-
299
- Args:
300
- ptr:
301
- Pointer offset to the currently bound buffer for this attribute.
302
-
303
- """
304
- if self._is_int:
305
- glVertexAttribIPointer(self.location, self.count, self.gl_type, self.stride, ptr)
306
- else:
307
- glVertexAttribPointer(self.location, self.count, self.gl_type, self.normalize, self.stride, ptr)
308
-
309
- def set_divisor(self) -> None:
310
- glVertexAttribDivisor(self.location, 1)
311
-
312
- def get_region(self, buffer: AttributeBufferObject, start: int, count: int) -> Array[CTypesDataType]:
313
- """Map a buffer region using this attribute as an accessor.
314
-
315
- The returned region consists of a contiguous array of component
316
- data elements. For example, if this attribute uses 3 floats per
317
- vertex, and the `count` parameter is 4, the number of floats mapped
318
- will be ``3 * 4 = 12``.
319
-
320
- Args:
321
- buffer:
322
- The buffer to map.
323
- start:
324
- Offset of the first vertex to map.
325
- count:
326
- Number of vertices to map
327
- """
328
- return buffer.get_region(start, count)
329
-
330
- def set_region(self, buffer: AttributeBufferObject, start: int, count: int, data: Sequence[float]) -> None:
331
- """Set the data over a region of the buffer.
332
-
333
- Args:
334
- buffer:
335
- The buffer to map.
336
- start:
337
- Offset of the first vertex to map.
338
- count:
339
- Number of vertices to map
340
- data:
341
- A sequence of data components.
342
-
343
- """
344
- buffer.set_region(start, count, data)
345
-
346
- def __repr__(self) -> str:
347
- return f"Attribute(name='{self.name}', location={self.location}, count={self.count})"
348
-
349
-
350
- class _UniformArray:
351
- """Wrapper of the GLSL array data inside a Uniform.
352
-
353
- Allows access to get and set items for a more Pythonic implementation.
354
- Types with a length longer than 1 will be returned as tuples as an inner list would not support individual value
355
- reassignment. Array data must either be set in full, or by indexing.
356
- """
357
- _uniform: _Uniform
358
- _gl_type: GLDataType
359
- _gl_getter: GLFunc
360
- _gl_setter: GLFunc
361
- _is_matrix: bool
362
- _dsa: bool
363
- _c_array: Array[GLDataType]
364
- _ptr: CTypesPointer[GLDataType]
365
- _idx_to_loc: dict[int, int]
366
-
367
- __slots__ = (
368
- '_c_array',
369
- '_dsa',
370
- '_gl_getter',
371
- '_gl_setter',
372
- '_gl_type',
373
- '_idx_to_loc',
374
- '_is_matrix',
375
- '_ptr',
376
- '_uniform',
377
- )
378
-
379
- def __init__(self, uniform: _Uniform, gl_getter: GLFunc, gl_setter: GLFunc, gl_type: GLDataType, is_matrix: bool,
380
- dsa: bool) -> None:
381
- self._uniform = uniform
382
- self._gl_type = gl_type
383
- self._gl_getter = gl_getter
384
- self._gl_setter = gl_setter
385
- self._is_matrix = is_matrix
386
- self._idx_to_loc = {} # Array index to uniform location mapping.
387
- self._dsa = dsa
388
-
389
- if self._uniform.length > 1:
390
- self._c_array = (gl_type * self._uniform.length * self._uniform.size)()
391
- else:
392
- self._c_array = (gl_type * self._uniform.size)()
393
-
394
- self._ptr = cast(self._c_array, POINTER(gl_type))
395
-
396
- def _get_location_for_index(self, index: int) -> int:
397
- """Get the location for the array name.
398
-
399
- It is not guaranteed that the location ID's of the uniform in the shader program will be a contiguous offset.
400
-
401
- On MacOS, the location ID of index 0 may be 1, and then index 2 might be 5. Whereas on Windows it may be a 1:1
402
- offset from 0 to index. Here, we store the location ID's of each index to ensure we are setting data on the
403
- right location.
404
- """
405
- loc = gl.glGetUniformLocation(self._uniform.program,
406
- create_string_buffer(f"{self._uniform.name}[{index}]".encode()))
407
- return loc
408
-
409
- def _get_array_loc(self, index: int) -> int:
410
- try:
411
- return self._idx_to_loc[index]
412
- except KeyError:
413
- loc = self._idx_to_loc[index] = self._get_location_for_index(index)
414
-
415
- if loc == -1:
416
- msg = f"{self._uniform.name}[{index}] not found.\nThis may have been optimized out by the OpenGL driver if unused."
417
- raise ShaderException(msg)
418
-
419
- return loc
420
-
421
- def __len__(self) -> int:
422
- return self._uniform.size
423
-
424
- def __delitem__(self, key: int) -> None:
425
- msg = "Deleting items is not support for UniformArrays."
426
- raise ShaderException(msg)
427
-
428
- def __getitem__(self, key: slice | int) -> list[tuple] | tuple:
429
- # Return as a tuple. Returning as a list may imply setting inner list elements will update values.
430
- if isinstance(key, slice):
431
- sliced_data = self._c_array[key]
432
- if self._uniform.length > 1:
433
- return [tuple(data) for data in sliced_data]
434
-
435
- return tuple([data for data in sliced_data]) # noqa: C416
436
-
437
- try:
438
- value = self._c_array[key]
439
- return tuple(value) if self._uniform.length > 1 else value
440
- except IndexError:
441
- msg = f"{self._uniform.name}[{key}] not found. This may have been optimized out by the OpenGL driver if unused."
442
- raise ShaderException(msg)
443
-
444
- def __setitem__(self, key: slice | int, value: Sequence) -> None:
445
- if isinstance(key, slice):
446
- self._c_array[key] = value
447
- self._update_uniform(self._ptr)
448
- return
449
-
450
- self._c_array[key] = value
451
-
452
- if self._uniform.length > 1:
453
- assert len(
454
- value) == self._uniform.length, (f"Setting this key requires {self._uniform.length} values, "
455
- f"received {len(value)}.")
456
- data = (self._gl_type * self._uniform.length)(*value)
457
- else:
458
- data = self._gl_type(value)
459
-
460
- self._update_uniform(data, offset=key)
461
-
462
- def get(self) -> _UniformArray:
463
- self._gl_getter(self._uniform.program, self._uniform.location, self._ptr)
464
- return self
465
-
466
- def set(self, values: Sequence) -> None:
467
- assert len(self._c_array) == len(
468
- values), f"Size of data ({len(values)}) does not match size of the uniform: {len(self._c_array)}."
469
-
470
- self._c_array[:] = values
471
- self._update_uniform(self._ptr)
472
-
473
- def _update_uniform(self, data: Sequence, offset: int = 0) -> None:
474
- if offset != 0:
475
- size = 1
476
- else:
477
- size = self._uniform.size
478
-
479
- location = self._get_location_for_index(offset)
480
-
481
- if self._dsa:
482
- if self._is_matrix:
483
- self._gl_setter(self._uniform.program, location, size, GL_FALSE, data)
484
- else:
485
- self._gl_setter(self._uniform.program, location, size, data)
486
- else:
487
- glUseProgram(self._uniform.program)
488
- if self._is_matrix:
489
- self._gl_setter(location, size, GL_FALSE, data)
490
- else:
491
- self._gl_setter(location, size, data)
492
-
493
- def __repr__(self) -> str:
494
- data = [tuple(data) if self._uniform.length > 1 else data for data in self._c_array]
495
- return f"UniformArray(uniform={self._uniform}, data={data})"
496
-
497
21
 
498
- _gl_matrices: tuple[int, ...] = (
499
- gl.GL_FLOAT_MAT2, gl.GL_FLOAT_MAT2x3, gl.GL_FLOAT_MAT2x4,
500
- gl.GL_FLOAT_MAT3, gl.GL_FLOAT_MAT3x2, gl.GL_FLOAT_MAT3x4,
501
- gl.GL_FLOAT_MAT4, gl.GL_FLOAT_MAT4x2, gl.GL_FLOAT_MAT4x3,
502
- )
503
-
504
-
505
- class _Uniform:
506
- type: int
507
- size: int
508
- location: int
509
- program: int
22
+ # NormalizedType = Literal[
23
+ # '', # no normalization.
24
+ # 'n', # Signed normalization, (-1, 1) # Not sure if OpenGL has this.
25
+ # 'N', # Unsigned normalization. (0, 1)
26
+ # ]
27
+ GLSLDataTypes = Literal[
28
+ 'mat4', # 4x4 matrix (16 floats)
29
+ 'vec4', # vec4 (4 floats)
30
+ 'vec3', # vec3 (3 floats)
31
+ 'vec2', # vec2 (2 floats)
32
+ 'float', # single float
33
+ 'int', # single int
34
+ 'uint', # single unsigned int
35
+ 'bool', # seems to be c_uint in glsl.
36
+ ]
37
+
38
+ UniformDataType = str
39
+ UniformName = str
40
+
41
+ class UniformBlockDesc(Protocol):
42
+ stages: tuple[ShaderType]
43
+ bind_num: int # binding number in descriptor set
44
+ set_num: int # descriptor set number
45
+ uniforms: tuple[tuple[UniformDataType, UniformName]]
46
+
47
+ @dataclass
48
+ class PushConstants:
49
+ stages: tuple[ShaderType]
50
+ constants: list[tuple[str, GLSLDataTypes]] # Name, GLSL Type
51
+
52
+
53
+ @dataclass
54
+ class Sampler:
510
55
  name: str
511
- length: int
512
- get: Callable[[], Array[GLDataType] | GLDataType]
513
- set: Callable[[float], None] | Callable[[Sequence], None]
514
-
515
- __slots__ = 'count', 'get', 'length', 'location', 'name', 'program', 'set', 'size', 'type'
516
-
517
- def __init__(self, program: int, name: str, uniform_type: int, size: int, location: int, dsa: bool) -> None:
518
- self.name = name
519
- self.type = uniform_type
520
- self.size = size
521
- self.location = location
522
- self.program = program
523
-
524
- gl_type, gl_setter_legacy, gl_setter_dsa, length = _uniform_setters[uniform_type]
525
- gl_setter = gl_setter_dsa if dsa else gl_setter_legacy
526
- gl_getter = _uniform_getters[gl_type]
527
-
528
- # Argument length of data
529
- self.length = length
530
-
531
- is_matrix = uniform_type in _gl_matrices
532
-
533
- # If it's an array, use the wrapper object.
534
- if size > 1:
535
- array = _UniformArray(self, gl_getter, gl_setter, gl_type, is_matrix, dsa)
536
- self.get = array.get
537
- self.set = array.set
538
- else:
539
- c_array: Array[GLDataType] = (gl_type * length)()
540
- ptr = cast(c_array, POINTER(gl_type))
541
-
542
- self.get = self._create_getter_func(program, location, gl_getter, c_array, length)
543
- self.set = self._create_setter_func(program, location, gl_setter, c_array, length, ptr, is_matrix, dsa)
544
-
545
- @staticmethod
546
- def _create_getter_func(program_id: int, location: int, gl_getter: GLFunc, c_array: Array[GLDataType],
547
- length: int) -> Callable[[], Array[GLDataType] | GLDataType]:
548
- """Factory function for creating simplified Uniform getters."""
549
- if length == 1:
550
- def getter_func() -> GLDataType:
551
- gl_getter(program_id, location, c_array)
552
- return c_array[0]
553
- else:
554
- def getter_func() -> Array[GLDataType]:
555
- gl_getter(program_id, location, c_array)
556
- return c_array[:]
557
-
558
- return getter_func
559
-
560
- @staticmethod
561
- def _create_setter_func(program_id: int, location: int, gl_setter: GLFunc, c_array: Array[GLDataType], length: int,
562
- ptr: CTypesPointer[GLDataType], is_matrix: bool, dsa: bool) -> Callable[[float], None]:
563
- """Factory function for creating simplified Uniform setters."""
564
- if dsa: # Bindless updates:
565
-
566
- if is_matrix:
567
- def setter_func(value: float) -> None:
568
- c_array[:] = value
569
- gl_setter(program_id, location, 1, GL_FALSE, ptr)
570
- elif length == 1:
571
- def setter_func(value: float) -> None:
572
- c_array[0] = value
573
- gl_setter(program_id, location, 1, ptr)
574
- elif length > 1:
575
- def setter_func(values: float) -> None:
576
- c_array[:] = values
577
- gl_setter(program_id, location, 1, ptr)
578
-
579
- else:
580
- msg = "Uniform type not yet supported."
581
- raise ShaderException(msg)
582
-
583
- return setter_func
584
-
585
- if is_matrix:
586
- def setter_func(value: float) -> None:
587
- glUseProgram(program_id)
588
- c_array[:] = value
589
- gl_setter(location, 1, GL_FALSE, ptr)
590
- elif length == 1:
591
- def setter_func(value: float) -> None:
592
- glUseProgram(program_id)
593
- c_array[0] = value
594
- gl_setter(location, 1, ptr)
595
- elif length > 1:
596
- def setter_func(values: float) -> None:
597
- glUseProgram(program_id)
598
- c_array[:] = values
599
- gl_setter(location, 1, ptr)
600
- else:
601
- msg = "Uniform type not yet supported."
602
- raise ShaderException(msg)
603
-
604
- return setter_func
605
-
606
- def __repr__(self) -> str:
607
- return f"Uniform(type={self.type}, size={self.size}, location={self.location})"
608
-
609
-
610
- def get_maximum_binding_count() -> int:
611
- """The maximum binding value that can be used for this hardware."""
612
- val = gl.GLint()
613
- gl.glGetIntegerv(gl.GL_MAX_UNIFORM_BUFFER_BINDINGS, byref(val))
614
- return val.value
615
-
616
-
617
- class _UBOBindingManager:
618
- """Manages the global Uniform Block binding assignments in the OpenGL context."""
619
- _in_use: set[int]
620
- _pool: list[int]
621
- _max_binding_count: int
622
- _ubo_names: dict[str, int]
623
- _ubo_programs: defaultdict[Any, weakref.WeakSet[ShaderProgram]]
624
-
625
- def __init__(self) -> None:
626
- self._ubo_programs = defaultdict(weakref.WeakSet)
627
- # Reserve 'WindowBlock' for 0.
628
- self._ubo_names = {'WindowBlock': 0}
629
- self._max_binding_count = get_maximum_binding_count()
630
- self._pool = list(range(1, self._max_binding_count))
631
- self._in_use = {0}
632
-
633
- @property
634
- def max_value(self) -> int:
635
- return self._max_binding_count
636
-
637
- def get_name(self, binding: int) -> str | None:
638
- """Return the uniform name associated with the binding number."""
639
- for name, current_binding in self._ubo_names.items():
640
- if binding == current_binding:
641
- return name
642
- return None
643
-
644
- def binding_exists(self, binding: int) -> bool:
645
- """Check if a binding index value is in use."""
646
- return binding in self._in_use
647
-
648
- def add_explicit_binding(self, shader_program: ShaderProgram, ub_name: str, binding: int) -> None:
649
- """Used when a uniform block has set its own binding point."""
650
- self._ubo_programs[ub_name].add(shader_program)
651
- self._ubo_names[ub_name] = binding
652
- if binding in self._pool:
653
- self._pool.remove(binding)
654
- self._in_use.add(binding)
655
-
656
- def get_binding(self, shader_program: ShaderProgram, ub_name: str) -> int:
657
- """Retrieve a global Uniform Block Binding ID value."""
658
- self._ubo_programs[ub_name].add(shader_program)
659
-
660
- if ub_name in self._ubo_names:
661
- return self._ubo_names[ub_name]
662
-
663
- self._check_freed_bindings()
664
-
665
- binding = self._get_new_binding()
666
- self._ubo_names[ub_name] = binding
667
- return binding
668
-
669
- def _check_freed_bindings(self) -> None:
670
- """Find and remove any Uniform Block names that no longer have a shader in use."""
671
- for ubo_name in list(self._ubo_programs):
672
- if ubo_name != 'WindowBlock' and not self._ubo_programs[ubo_name]:
673
- del self._ubo_programs[ubo_name]
674
- # Return the binding number to the pool.
675
- self.return_binding(self._ubo_names[ubo_name])
676
- del self._ubo_names[ubo_name]
677
-
678
- def _get_new_binding(self) -> int:
679
- if not self._pool:
680
- msg = "All Uniform Buffer Bindings are in use."
681
- raise ValueError(msg)
682
-
683
- number = self._pool.pop(0)
684
- self._in_use.add(number)
685
- return number
686
-
687
- def return_binding(self, index: int) -> None:
688
- if index in self._in_use:
689
- self._pool.append(index)
690
- self._in_use.remove(index)
691
- else:
692
- msg = f"Uniform binding point: {index} is not in use."
693
- raise ValueError(msg)
694
-
695
- # Regular expression to detect array indices like [0], [1], etc.
696
- array_regex = re.compile(r"(\w+)\[(\d+)\]")
697
-
698
- class UniformBlock:
699
- program: CallableProxyType[Callable[..., Any] | Any] | Any
700
- name: str
701
- index: int
702
- size: int
56
+ desc_set: int
703
57
  binding: int
704
- uniforms: dict[int, tuple[str, GLDataType, int, int]]
705
- view_cls: type[Structure] | None
706
- __slots__ = 'binding', 'index', 'name', 'program', 'size', 'uniform_count', 'uniforms', 'view_cls'
707
-
708
- def __init__(self, program: ShaderProgram, name: str, index: int, size: int, binding: int,
709
- uniforms: dict[int, tuple[str, GLDataType, int, int]], uniform_count: int) -> None:
710
- """Initialize a uniform block for a ShaderProgram."""
711
- self.program = weakref.proxy(program)
712
- self.name = name
713
- self.index = index
714
- self.size = size
715
- self.binding = binding
716
- self.uniforms = uniforms
717
- self.uniform_count = uniform_count
718
- self.view_cls = None
719
-
720
- def create_ubo(self) -> UniformBufferObject:
721
- """Create a new UniformBufferObject from this uniform block."""
722
- if self.view_cls is None:
723
- self.view_cls = self._introspect_uniforms()
724
- return UniformBufferObject(self.view_cls, self.size, self.binding)
725
-
726
- def set_binding(self, binding: int) -> None:
727
- """Rebind the Uniform Block to a new binding index number.
728
-
729
- This only affects the program this Uniform Block is derived from.
730
-
731
- Binding value of 0 is reserved for the Pyglet's internal uniform block named ``WindowBlock``.
732
-
733
- .. warning:: By setting a binding manually, the user is expected to manage all Uniform Block bindings
734
- for all shader programs manually. Since the internal global ID's will be unaware of changes set
735
- by this function, collisions may occur if you use a lower number.
736
-
737
- .. note:: You must call ``create_ubo`` to get another Uniform Buffer Object after calling this,
738
- as the previous buffers are still bound to the old binding point.
739
- """
740
- assert binding != 0, "Binding 0 is reserved for the internal Pyglet 'WindowBlock'."
741
- assert pyglet.gl.current_context is not None, "No context available."
742
- manager: _UBOBindingManager = pyglet.gl.current_context.ubo_manager
743
- if binding >= manager.max_value:
744
- msg = f"Binding value exceeds maximum allowed by hardware: {manager.max_value}"
745
- raise ShaderException(msg)
746
- existing_name = manager.get_name(binding)
747
- if existing_name and existing_name != self.name:
748
- msg = f"Binding: {binding} was in use by {existing_name}, and has been overridden."
749
- warnings.warn(msg)
750
-
751
- self.binding = binding
752
- gl.glUniformBlockBinding(self.program.id, self.index, self.binding)
753
-
754
- def _introspect_uniforms(self) -> type[Structure]:
755
- """Introspect the block's structure and return a ctypes struct for manipulating the uniform block's members."""
756
- p_id = self.program.id
757
- index = self.index
758
-
759
- active_count = self.uniform_count
760
-
761
- # Query the uniform index order and each uniform's offset:
762
- indices = (gl.GLuint * active_count)()
763
- offsets = (gl.GLint * active_count)()
764
- indices_ptr = cast(addressof(indices), POINTER(gl.GLint))
765
- offsets_ptr = cast(addressof(offsets), POINTER(gl.GLint))
766
- gl.glGetActiveUniformBlockiv(p_id, index, gl.GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices_ptr)
767
- gl.glGetActiveUniformsiv(p_id, active_count, indices, gl.GL_UNIFORM_OFFSET, offsets_ptr)
768
-
769
- # Offsets may be returned in non-ascending order, sort them with the corresponding index:
770
- _oi = sorted(zip(offsets, indices), key=lambda x: x[0])
771
- offsets = [x[0] for x in _oi] + [self.size]
772
- indices = (gl.GLuint * active_count)(*(x[1] for x in _oi))
773
-
774
- # # Query other uniform information:
775
- # gl_types = (gl.GLint * active_count)()
776
- # mat_stride = (gl.GLint * active_count)()
777
- # gl_types_ptr = cast(addressof(gl_types), POINTER(gl.GLint))
778
- # stride_ptr = cast(addressof(mat_stride), POINTER(gl.GLint))
779
- # gl.glGetActiveUniformsiv(p_id, active_count, indices, gl.GL_UNIFORM_TYPE, gl_types_ptr)
780
- # gl.glGetActiveUniformsiv(p_id, active_count, indices, gl.GL_UNIFORM_MATRIX_STRIDE, stride_ptr)
781
-
782
- array_sizes = {}
783
- dynamic_structs = {}
784
- p_count = 0
785
-
786
- rep_func = lambda s: str(dict(s._fields_))
787
-
788
- def build_ctypes_struct(name: str, struct_dict: dict) -> type:
789
- fields = []
790
- for field_name, field_type in struct_dict.items():
791
- if isinstance(field_type, dict):
792
- # Recursive call for nested structures
793
- element_struct = build_ctypes_struct(field_name, field_type)
794
- field_type = element_struct # noqa: PLW2901
795
- if field_name in array_sizes and array_sizes[field_name] > 1:
796
- field_type = element_struct * array_sizes[field_name] # noqa: PLW2901
797
- else:
798
- # This handles base types like c_float_Array_2, which isn't a dict.
799
- fields.append((field_name, field_type))
800
- continue
801
- fields.append((field_name, field_type))
802
-
803
- return type(name.title(), (Structure,), {"_fields_": fields, "__repr__": rep_func})
804
-
805
- # Build a ctypes Structure of the uniforms including arrays and nested structures.
806
- for i in range(active_count):
807
- u_name, gl_type, length, u_size = self.uniforms[indices[i]]
808
-
809
- parts = u_name.split(".")
810
-
811
- current_structure = dynamic_structs
812
- for part_idx, part in enumerate(parts):
813
- part_name = part
814
- match = array_regex.match(part_name)
815
- if match: # It's an array
816
- arr_name, index = match.groups()
817
- part_name = arr_name
818
-
819
- if part_idx != len(parts) - 1:
820
- index = int(index) # Convert the index to an integer
821
-
822
- # Track array sizes for the current array name
823
- array_sizes[arr_name] = max(array_sizes.get(arr_name, 0), index + 1)
824
- if array_sizes[arr_name] > 1:
825
- break
826
-
827
- if arr_name not in current_structure:
828
- current_structure[arr_name] = {}
829
-
830
- current_structure = current_structure[arr_name] # Move to the correct index of the array
831
- continue
832
-
833
- # The end should be a regular attribute
834
- if part_idx == len(parts) - 1: # The last part is the actual type
835
- if u_size > 1:
836
- # If size > 1, treat as an array of type
837
- if length > 1:
838
- current_structure[part_name] = (gl_type * length) * u_size
839
- else:
840
- current_structure[part_name] = gl_type * u_size
841
- else:
842
- if length > 1:
843
- current_structure[part_name] = gl_type * length
844
- else:
845
- current_structure[part_name] = gl_type
846
-
847
- offset_size = offsets[i + 1] - offsets[i]
848
- c_type_size = sizeof(current_structure[part_name])
849
- padding = offset_size - c_type_size
850
-
851
- # TODO: Cannot get a different stride on my hardware. Needs testing.
852
- # is_matrix = gl_types[i] in _gl_matrices
853
- # if is_matrix:
854
- # stride_padding = (mat_stride[i] // 4) * 4 - offset_size
855
- # if stride_padding > 0:
856
- # view_fields.append((f'_matrix_stride{i}', c_byte * stride_padding))
857
-
858
- if padding > 0:
859
- current_structure[f'_padding{p_count}'] = c_byte * padding
860
- p_count += 1
861
- else:
862
- if part_name not in current_structure:
863
- current_structure[part_name] = {}
864
- current_structure = current_structure[part_name] # Drill down into nested structures
865
-
866
- # Custom ctypes Structure for Uniform access:
867
- return build_ctypes_struct('View', dynamic_structs)
868
-
869
- def _actual_binding_point(self) -> int:
870
- """Queries OpenGL to find what the bind point currently is."""
871
- binding = gl.GLint()
872
- gl.glGetActiveUniformBlockiv(self.program.id, self.index, gl.GL_UNIFORM_BLOCK_BINDING, binding)
873
- return binding.value
874
-
875
- def __repr__(self) -> str:
876
- return (f"{self.__class__.__name__}(program={self.program.id}, location={self.index}, size={self.size}, "
877
- f"binding={self.binding})")
878
-
879
-
880
- class UniformBufferObject:
881
- buffer: BufferObject
882
- view: Structure
883
- _view_ptr: CTypesPointer[Structure]
884
- binding: int
885
- buffer: BufferObject
886
- __slots__ = '_view_ptr', 'binding', 'buffer', 'view'
887
-
888
- def __init__(self, view_class: type[Structure], buffer_size: int, binding: int) -> None:
889
- """Initialize the Uniform Buffer Object with the specified Structure."""
890
- self.buffer = BufferObject(buffer_size)
891
- self.view = view_class()
892
- self._view_ptr = pointer(self.view)
893
- self.binding = binding
894
-
895
- @property
896
- def id(self) -> int:
897
- """The buffer ID associated with this UBO."""
898
- return self.buffer.id
899
-
900
- def bind(self) -> None:
901
- """Bind this buffer to the bind point established by the UniformBuffer parent."""
902
- glBindBufferBase(GL_UNIFORM_BUFFER, self.binding, self.buffer.id)
903
-
904
- def read(self) -> bytes:
905
- """Read the byte contents of the buffer."""
906
- glBindBuffer(GL_ARRAY_BUFFER, self.buffer.id)
907
- ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0, self.buffer.size, GL_MAP_READ_BIT)
908
- data = string_at(ptr, size=self.buffer.size)
909
- glUnmapBuffer(GL_ARRAY_BUFFER)
910
- return data
911
-
912
- def __enter__(self) -> Structure:
913
- # Return the view to the user in a `with` context:
914
- return self.view
915
-
916
- def __exit__(self, _exc_type, _exc_val, _exc_tb) -> None: # noqa: ANN001
917
- self.bind()
918
- self.buffer.set_data(self._view_ptr)
919
-
920
- def __repr__(self) -> str:
921
- return f"{self.__class__.__name__}(id={self.buffer.id}, binding={self.binding})"
922
-
923
-
924
- # Utility functions:
925
-
926
- def _get_number(program_id: int, variable_type: int) -> int:
927
- """Get the number of active variables of the passed GL type."""
928
- number = gl.GLint(0)
929
- glGetProgramiv(program_id, variable_type, byref(number))
930
- return number.value
931
-
932
-
933
- def _query_attribute(program_id: int, index: int) -> tuple[str, int, int]:
934
- """Query the name, type, and size of an Attribute by index."""
935
- asize = gl.GLint()
936
- atype = gl.GLenum()
937
- buf_size = 192
938
- aname = create_string_buffer(buf_size)
939
- try:
940
- glGetActiveAttrib(program_id, index, buf_size, None, asize, atype, aname)
941
- return aname.value.decode(), atype.value, asize.value
942
- except GLException as exc:
943
- raise ShaderException from exc
944
-
945
-
946
- def _introspect_attributes(program_id: int) -> dict[str, Any]:
947
- """Introspect a Program's Attributes, and return a dict of accessors."""
948
- attributes = {}
949
-
950
- for index in range(_get_number(program_id, gl.GL_ACTIVE_ATTRIBUTES)):
951
- a_name, a_type, a_size = _query_attribute(program_id, index)
952
- loc = gl.glGetAttribLocation(program_id, create_string_buffer(a_name.encode('utf-8')))
953
- if loc == -1: # not a user defined attribute
954
- continue
955
- count, fmt = _attribute_types[a_type]
956
- attributes[a_name] = {
957
- 'type': a_type, 'size': a_size, 'location': loc, 'count': count, 'format': fmt,
958
- 'instance': False,
959
- }
960
-
961
- if _debug_gl_shaders:
962
- for attribute in attributes.values():
963
- print(f" Found attribute: {attribute}")
964
-
965
- return attributes
966
-
967
-
968
- def _link_program(*shaders: Shader) -> int:
969
- """Link one or more Shaders into a ShaderProgram.
970
-
971
- Returns:
972
- The ID assigned to the linked ShaderProgram.
973
- """
974
- program_id = glCreateProgram()
975
- for shader in shaders:
976
- glAttachShader(program_id, shader.id)
977
- glLinkProgram(program_id)
978
-
979
- # Check the link status of program
980
- status = c_int()
981
- glGetProgramiv(program_id, GL_LINK_STATUS, byref(status))
982
- if not status.value:
983
- length = c_int()
984
- glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, length)
985
- log = c_buffer(length.value)
986
- glGetProgramInfoLog(program_id, len(log), None, log)
987
- msg = f"Error linking shader program:\n{log.value.decode()}"
988
- raise ShaderException(msg)
989
-
990
- # Shader objects no longer needed
991
- for shader in shaders:
992
- glDetachShader(program_id, shader.id)
993
-
994
- return program_id
995
-
996
-
997
- def _get_program_log(program_id: int) -> str:
998
- """Query a ShaderProgram link logs."""
999
- result = c_int(0)
1000
- glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, byref(result))
1001
- result_str = create_string_buffer(result.value)
1002
- glGetProgramInfoLog(program_id, result, None, result_str)
1003
-
1004
- if result_str.value:
1005
- return f"OpenGL returned the following message when linking the program: \n{result_str.value}"
1006
-
1007
- return f"Program '{program_id}' linked successfully."
1008
-
1009
-
1010
- def _query_uniform(program_id: int, index: int) -> tuple[str, int, int]:
1011
- """Query the name, type, and size of a Uniform by index."""
1012
- usize = gl.GLint()
1013
- utype = gl.GLenum()
1014
- buf_size = 192
1015
- uname = create_string_buffer(buf_size)
1016
- try:
1017
- gl.glGetActiveUniform(program_id, index, buf_size, None, usize, utype, uname)
1018
- return uname.value.decode(), utype.value, usize.value
1019
-
1020
- except GLException as exc:
1021
- raise ShaderException from exc
1022
-
1023
-
1024
- def _introspect_uniforms(program_id: int, have_dsa: bool) -> dict[str, _Uniform]:
1025
- """Introspect a Program's uniforms, and return a dict of accessors."""
1026
- uniforms = {}
1027
-
1028
- for index in range(_get_number(program_id, gl.GL_ACTIVE_UNIFORMS)):
1029
- u_name, u_type, u_size = _query_uniform(program_id, index)
1030
-
1031
- # Multidimensional arrays cannot be fully inspected via OpenGL calls and compile errors with 3.3.
1032
- array_count = u_name.count("[0]")
1033
- if array_count > 1 and u_name.count("[0][0]") != 0:
1034
- msg = "Multidimensional arrays are not currently supported."
1035
- raise ShaderException(msg)
1036
-
1037
- loc = gl.glGetUniformLocation(program_id, create_string_buffer(u_name.encode('utf-8')))
1038
- if loc == -1: # Skip uniforms that may be inside a Uniform Block
1039
- continue
1040
-
1041
- # Strip [0] from array name for a more user-friendly name.
1042
- if array_count != 0:
1043
- u_name = u_name.strip('[0]')
1044
-
1045
- assert u_name not in uniforms, f"{u_name} exists twice in the shader. Possible name clash with an array."
1046
- uniforms[u_name] = _Uniform(program_id, u_name, u_type, u_size, loc, have_dsa)
1047
-
1048
- if _debug_gl_shaders:
1049
- for uniform in uniforms.values():
1050
- print(f" Found uniform: {uniform}")
58
+ count: int = 1
59
+ stages: Sequence[ShaderType] = ("fragment",)
1051
60
 
1052
- return uniforms
1053
61
 
62
+ class ShaderProgramBase(ABC):
63
+ _attributes: dict[str, Attribute]
64
+ _uniform_blocks: dict[str, UniformBlockBase]
65
+ _samplers: dict[str, Sampler]
1054
66
 
1055
- def _get_uniform_block_name(program_id: int, index: int) -> str:
1056
- """Query the name of a Uniform Block, by index."""
1057
- buf_size = 128
1058
- size = c_int(0)
1059
- name_buf = create_string_buffer(buf_size)
1060
- try:
1061
- gl.glGetActiveUniformBlockName(program_id, index, buf_size, size, name_buf)
1062
- return name_buf.value.decode()
1063
- except GLException:
1064
- msg = f"Unable to query UniformBlock name at index: {index}"
1065
- raise ShaderException(msg) # noqa: B904
1066
-
1067
-
1068
- def _introspect_uniform_blocks(program: ShaderProgram | ComputeShaderProgram) -> dict[str, UniformBlock]:
1069
- uniform_blocks = {}
1070
- program_id = program.id
1071
-
1072
- for index in range(_get_number(program_id, gl.GL_ACTIVE_UNIFORM_BLOCKS)):
1073
- name = _get_uniform_block_name(program_id, index)
1074
-
1075
- num_active = gl.GLint()
1076
- block_data_size = gl.GLint()
1077
- binding = gl.GLint()
1078
-
1079
- gl.glGetActiveUniformBlockiv(program_id, index, gl.GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, num_active)
1080
- gl.glGetActiveUniformBlockiv(program_id, index, gl.GL_UNIFORM_BLOCK_DATA_SIZE, block_data_size)
1081
- gl.glGetActiveUniformBlockiv(program_id, index, gl.GL_UNIFORM_BLOCK_BINDING, binding)
1082
-
1083
- indices = (gl.GLuint * num_active.value)()
1084
- indices_ptr = cast(addressof(indices), POINTER(gl.GLint))
1085
- gl.glGetActiveUniformBlockiv(program_id, index, gl.GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices_ptr)
1086
-
1087
- uniforms: dict[int, tuple[str, GLDataType, int, int]] = {}
1088
-
1089
- if not hasattr(pyglet.gl.current_context, "ubo_manager"):
1090
- pyglet.gl.current_context.ubo_manager = _UBOBindingManager()
1091
-
1092
- manager = pyglet.gl.current_context.ubo_manager
1093
-
1094
- for block_uniform_index in indices:
1095
- uniform_name, u_type, u_size = _query_uniform(program_id, block_uniform_index)
1096
-
1097
- # Remove block name.
1098
- if uniform_name.startswith(f"{name}."):
1099
- uniform_name = uniform_name[len(name) + 1:] # Strip 'block_name.' part
1100
-
1101
- if uniform_name.count("[0][0]") > 0:
1102
- msg = "Multidimensional arrays are not currently supported."
1103
- raise ShaderException(msg)
1104
-
1105
- gl_type, _, _, length = _uniform_setters[u_type]
1106
- uniforms[block_uniform_index] = (uniform_name, gl_type, length, u_size)
1107
-
1108
- binding_index = binding.value
1109
- if pyglet.options.shader_bind_management:
1110
- # If no binding is specified in GLSL, then assign it internally.
1111
- if binding.value == 0:
1112
- binding_index = manager.get_binding(program, name)
1113
-
1114
- # This might cause an error if index > GL_MAX_UNIFORM_BUFFER_BINDINGS, but surely no
1115
- # one would be crazy enough to use more than 36 uniform blocks, right?
1116
- gl.glUniformBlockBinding(program_id, index, binding_index)
1117
- else:
1118
- # If a binding was manually set in GLSL, just check if the values collide to warn the user.
1119
- _block_name = manager.get_name(binding.value)
1120
- if _block_name and _block_name != name:
1121
- msg = (f"{program} explicitly set '{name}' to {binding.value} in the shader. '{_block_name}' has "
1122
- f"been overridden.")
1123
- warnings.warn(msg)
1124
- manager.add_explicit_binding(program, name, binding.value)
1125
-
1126
- uniform_blocks[name] = UniformBlock(program, name, index, block_data_size.value, binding_index, uniforms,
1127
- len(indices))
1128
-
1129
- if _debug_gl_shaders:
1130
- for block in uniform_blocks.values():
1131
- print(f" Found uniform block: {block}")
1132
-
1133
- return uniform_blocks
1134
-
1135
-
1136
- # Shader & program classes:
1137
-
1138
- class ShaderSource:
1139
- """GLSL source container for making source parsing simpler.
1140
-
1141
- We support locating out attributes and applying #defines values.
1142
-
1143
- .. note:: We do assume the source is neat enough to be parsed this way and doesn't contain several statements in
1144
- one line.
1145
- """
1146
- _type: gl.GLenum
1147
- _lines: list[str]
1148
-
1149
- def __init__(self, source: str, source_type: gl.GLenum) -> None:
1150
- """Create a shader source wrapper."""
1151
- self._lines = source.strip().splitlines()
1152
- self._type = source_type
1153
-
1154
- if not self._lines:
1155
- msg = "Shader source is empty"
1156
- raise ShaderException(msg)
1157
-
1158
- self._version = self._find_glsl_version()
1159
-
1160
- if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
1161
- self._lines[0] = "#version 310 es"
1162
- self._lines.insert(1, "precision mediump float;")
1163
-
1164
- if self._type == gl.GL_GEOMETRY_SHADER:
1165
- self._lines.insert(1, "#extension GL_EXT_geometry_shader : require")
1166
-
1167
- if self._type == gl.GL_COMPUTE_SHADER:
1168
- self._lines.insert(1, "precision mediump image2D;")
1169
-
1170
- self._version = self._find_glsl_version()
1171
-
1172
- def validate(self) -> str:
1173
- """Return the validated shader source."""
1174
- return "\n".join(self._lines)
1175
-
1176
- def _find_glsl_version(self) -> int:
1177
- if self._lines[0].strip().startswith("#version"):
1178
- try:
1179
- return int(self._lines[0].split()[1])
1180
- except (ValueError, IndexError):
1181
- pass
1182
-
1183
- source = "\n".join(f"{str(i + 1).zfill(3)}: {line} " for i, line in enumerate(self._lines))
1184
-
1185
- msg = (
1186
- "Cannot find #version flag in shader source. "
1187
- "A #version statement is required on the first line.\n"
1188
- "------------------------------------\n"
1189
- f"{source}"
1190
- )
1191
- raise ShaderException(msg)
1192
-
1193
-
1194
- class Shader:
1195
- """OpenGL shader.
1196
-
1197
- Shader objects are compiled on instantiation.
1198
- You can reuse a Shader object in multiple ShaderPrograms.
1199
- """
1200
- _context: Context | None
1201
- _id: int | None
1202
- type: ShaderType
1203
-
1204
- def __init__(self, source_string: str, shader_type: ShaderType) -> None:
1205
- """Initialize a shader type.
1206
-
1207
- Args:
1208
- source_string:
1209
- A string containing the source of your shader program.
1210
-
1211
- shader_type:
1212
- A string containing the type of shader program:
1213
-
1214
- * ``'vertex'``
1215
- * ``'fragment'``
1216
- * ``'geometry'``
1217
- * ``'compute'``
1218
- * ``'tesscontrol'``
1219
- * ``'tessevaluation'``
1220
- """
1221
- self._context = pyglet.gl.current_context
67
+ def __init__(self, *shaders: ShaderBase) -> None:
1222
68
  self._id = None
1223
- self.type = shader_type
1224
-
1225
- try:
1226
- shader_type = _shader_types[shader_type]
1227
- except KeyError as err:
1228
- msg = (
1229
- f"shader_type '{shader_type}' is invalid."
1230
- f"Valid types are: {list(_shader_types)}"
1231
- )
1232
- raise ShaderException(msg) from err
1233
-
1234
- source_string = ShaderSource(source_string, shader_type).validate()
1235
- shader_source_utf8 = source_string.encode("utf8")
1236
- source_buffer_pointer = cast(c_char_p(shader_source_utf8), POINTER(c_char))
1237
- source_length = c_int(len(shader_source_utf8))
1238
-
1239
- shader_id = gl.glCreateShader(shader_type)
1240
- self._id = shader_id
1241
- gl.glShaderSource(shader_id, 1, byref(source_buffer_pointer), source_length)
1242
- gl.glCompileShader(shader_id)
1243
69
 
1244
- status = c_int(0)
1245
- gl.glGetShaderiv(shader_id, gl.GL_COMPILE_STATUS, byref(status))
1246
-
1247
- if status.value != GL_TRUE:
1248
- source = self._get_shader_source(shader_id)
1249
- source_lines = "{0}".format("\n".join(f"{str(i + 1).zfill(3)}: {line} "
1250
- for i, line in enumerate(source.split("\n"))))
70
+ assert shaders, "At least one Shader object is required."
1251
71
 
1252
- msg = (f"\n------------------------------------------------------------\n"
1253
- f"{source_lines}"
1254
- f"\n------------------------------------------------------------\n"
1255
- f"Shader compilation failed. Please review the error on the specified line.\n"
1256
- f"{self._get_shader_log(shader_id)}")
72
+ # Attribute description
73
+ self._attributes = {}
1257
74
 
1258
- raise ShaderException(msg)
1259
-
1260
- if _debug_gl_shaders:
1261
- print(self._get_shader_log(shader_id))
75
+ # Uniform Block description
76
+ self._uniform_blocks = {}
1262
77
 
1263
78
  @property
1264
- def id(self) -> int:
79
+ def id(self):
1265
80
  return self._id
1266
81
 
1267
- def _get_shader_log(self, shader_id: int) -> str:
1268
- log_length = c_int(0)
1269
- gl.glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, byref(log_length))
1270
- result_str = create_string_buffer(log_length.value)
1271
- gl.glGetShaderInfoLog(shader_id, log_length, None, result_str)
1272
- if result_str.value:
1273
- return (f"OpenGL returned the following message when compiling the "
1274
- f"'{self.type}' shader: \n{result_str.value.decode('utf8')}")
82
+ @property
83
+ def is_defined(self) -> bool:
84
+ """Determine if the ShaderProgram was defined and is ready for use."""
85
+ # Just use the attributes are filled in to determine if it's ready.
86
+ return bool(self._attributes)
1275
87
 
1276
- return f"{self.type.capitalize()} Shader '{shader_id}' compiled successfully."
88
+ def set_attributes(self, *attributes: Attribute) -> None:
89
+ """Define the attributes of the vertex shader.
1277
90
 
1278
- @staticmethod
1279
- def _get_shader_source(shader_id: int) -> str:
1280
- """Get the shader source from the shader object."""
1281
- source_length = c_int(0)
1282
- gl.glGetShaderiv(shader_id, gl.GL_SHADER_SOURCE_LENGTH, source_length)
1283
- source_str = create_string_buffer(source_length.value)
1284
- gl.glGetShaderSource(shader_id, source_length, None, source_str)
1285
- return source_str.value.decode('utf8')
1286
-
1287
- def delete(self) -> None:
1288
- """Deletes the shader.
1289
-
1290
- This cannot be undone.
91
+ On some backends like OpenGL, this is unnecessary unless you want to redefine the buffers.
1291
92
  """
1292
- glDeleteShader(self._id)
1293
- self._id = None
1294
-
1295
- def __del__(self) -> None:
1296
- if self._id is not None:
1297
- try:
1298
- self._context.delete_shader(self._id)
1299
- if _debug_gl_shaders:
1300
- print(f"Destroyed {self.type} Shader '{self._id}'")
1301
- self._id = None
1302
- except (AttributeError, ImportError):
1303
- pass # Interpreter is shutting down
93
+ for attrib in attributes:
94
+ self._attributes[attrib.name] = attrib
1304
95
 
1305
- def __repr__(self) -> str:
1306
- return f"{self.__class__.__name__}(id={self.id}, type={self.type})"
96
+ def set_uniform_blocks(self, *uniform_blocks: UniformBlockDesc) -> None:
97
+ for ub in uniform_blocks:
98
+ self._uniform_blocks[ub.__class__.__name__] = self.get_uniform_block_cls()
1307
99
 
100
+ def set_samplers(self, *samplers: Sampler) -> None:
101
+ for sampler in samplers:
102
+ self._samplers[sampler.name] = sampler
1308
103
 
1309
- class ShaderProgram:
1310
- """OpenGL shader program."""
1311
- _id: int | None
1312
- _context: Context | None
1313
- _attributes: dict[str, Any]
1314
- _uniforms: dict[str, _Uniform]
1315
- _uniform_blocks: dict[str, UniformBlock]
1316
-
1317
- __slots__ = '__weakref__', '_attributes', '_context', '_id', '_uniform_blocks', '_uniforms'
1318
-
1319
- def __init__(self, *shaders: Shader) -> None:
1320
- """Initialize the ShaderProgram using at least two Shader instances."""
1321
- self._id = None
1322
-
1323
- assert shaders, "At least one Shader object is required."
1324
- self._id = _link_program(*shaders)
1325
- self._context = pyglet.gl.current_context
1326
-
1327
- if _debug_gl_shaders:
1328
- print(_get_program_log(self._id))
1329
-
1330
- # Query if Direct State Access is available:
1331
- have_dsa = gl_info.have_version(4, 1) or gl_info.have_extension("GL_ARB_separate_shader_objects")
1332
- self._attributes = _introspect_attributes(self._id)
1333
- self._uniforms = _introspect_uniforms(self._id, have_dsa)
1334
- self._uniform_blocks = _introspect_uniform_blocks(self)
1335
-
1336
- @property
1337
- def id(self) -> int:
1338
- return self._id
104
+ def get_uniform_block_cls(self) -> type[UniformBlockBase]:
105
+ return UniformBlockBase
1339
106
 
1340
107
  @property
1341
108
  def attributes(self) -> dict[str, Any]:
@@ -1348,22 +115,7 @@ class ShaderProgram:
1348
115
  return self._attributes.copy()
1349
116
 
1350
117
  @property
1351
- def uniforms(self) -> dict[str, Any]:
1352
- """Uniform metadata dictionary.
1353
-
1354
- This property returns a dictionary containing metadata of all
1355
- Uniforms that were introspected in this ShaderProgram. Modifying
1356
- this dictionary has no effect. To set or get a uniform, the uniform
1357
- name is used as a key on the ShaderProgram instance. For example::
1358
-
1359
- my_shader_program[uniform_name] = 123
1360
- value = my_shader_program[uniform_name]
1361
-
1362
- """
1363
- return {n: {'location': u.location, 'length': u.length, 'size': u.size} for n, u in self._uniforms.items()}
1364
-
1365
- @property
1366
- def uniform_blocks(self) -> dict[str, UniformBlock]:
118
+ def uniform_blocks(self) -> dict[str, UniformBlockBase]:
1367
119
  """A dictionary of introspected UniformBlocks.
1368
120
 
1369
121
  This property returns a dictionary of
@@ -1376,118 +128,12 @@ class ShaderProgram:
1376
128
  """
1377
129
  return self._uniform_blocks
1378
130
 
1379
- def use(self) -> None:
1380
- glUseProgram(self._id)
1381
-
1382
- @staticmethod
1383
- def stop() -> None:
1384
- glUseProgram(0)
1385
-
1386
- __enter__ = use
1387
- bind = use
1388
- unbind = stop
1389
-
1390
- def __exit__(self, *_) -> None: # noqa: ANN002
1391
- glUseProgram(0)
1392
-
1393
- def delete(self) -> None:
1394
- glDeleteProgram(self._id)
1395
- self._id = None
1396
-
1397
- def __del__(self) -> None:
1398
- if self._id is not None:
1399
- try:
1400
- self._context.delete_shader_program(self._id)
1401
- self._id = None
1402
- except (AttributeError, ImportError):
1403
- pass # Interpreter is shutting down
1404
-
1405
- def __setitem__(self, key: str, value: Any) -> None:
1406
- try:
1407
- uniform = self._uniforms[key]
1408
- except KeyError as err:
1409
- msg = (f"A Uniform with the name `{key}` was not found.\n"
1410
- f"The spelling may be incorrect or, if not in use, it "
1411
- f"may have been optimized out by the OpenGL driver.")
1412
- if _debug_gl_shaders:
1413
- warnings.warn(msg)
1414
- return
1415
- raise ShaderException(msg) from err
1416
- try:
1417
- uniform.set(value)
1418
- except GLException as err:
1419
- raise ShaderException from err
1420
-
1421
- def __getitem__(self, item: str) -> Any:
1422
- try:
1423
- uniform = self._uniforms[item]
1424
- except KeyError as err:
1425
- msg = (f"A Uniform with the name `{item}` was not found.\n"
1426
- f"The spelling may be incorrect or, if not in use, it "
1427
- f"may have been optimized out by the OpenGL driver.")
1428
- if _debug_gl_shaders:
1429
- warnings.warn(msg)
1430
- return None
1431
-
1432
- raise ShaderException from err
1433
- try:
1434
- return uniform.get()
1435
- except GLException as err:
1436
- raise ShaderException from err
1437
-
1438
- def _vertex_list_create(self, count: int, mode: int, indices: Sequence[int] | None = None,
131
+ def _vertex_list_create(self, count: int, mode: GeometryMode, indices: Sequence[int] | None = None,
1439
132
  instances: Sequence[str] | None = None, batch: Batch = None, group: Group = None,
1440
- **data: Any) -> VertexList | IndexedVertexList:
1441
- attributes = self._attributes.copy()
1442
- initial_arrays = []
1443
-
1444
- instanced = instances is not None
1445
- indexed = indices is not None
1446
-
1447
- for name, fmt in data.items():
1448
- try:
1449
- if isinstance(fmt, tuple):
1450
- fmt, array = fmt # noqa: PLW2901
1451
- initial_arrays.append((name, array))
1452
- attributes[name] = {**attributes[name], 'format': fmt, 'instance': name in instances if instances else False}
1453
- except KeyError: # noqa: PERF203
1454
- if _debug_gl_shaders:
1455
- msg = (f"The attribute `{name}` was not found in the Shader Program.\n"
1456
- f"Please check the spelling, or it may have been optimized out by the OpenGL driver.\n"
1457
- f"Valid names: {list(attributes)}")
1458
- warnings.warn(msg)
1459
- continue
1460
-
1461
- if _debug_gl_shaders:
1462
- if missing_data := [key for key in attributes if key not in data]:
1463
- msg = (
1464
- f"No data was supplied for the following found attributes: `{missing_data}`.\n"
1465
- )
1466
- warnings.warn(msg)
1467
-
1468
- batch = batch or pyglet.graphics.get_default_batch()
1469
- group = group or pyglet.graphics.ShaderGroup(program=self)
1470
- domain = batch.get_domain(indexed, instanced, mode, group, attributes)
1471
-
1472
- # Create vertex list and initialize
1473
- if indexed:
1474
- vlist = domain.create(count, len(indices))
1475
- vlist.indices = indices
1476
- else:
1477
- vlist = domain.create(count)
1478
-
1479
- for name, array in initial_arrays:
1480
- try:
1481
- vlist.set_attribute_data(name, array)
1482
- except KeyError: # noqa: PERF203
1483
- continue
1484
-
1485
- if instanced:
1486
- vlist.instanced = True
1487
-
1488
- return vlist
1489
-
1490
- def vertex_list(self, count: int, mode: int, batch: Batch = None, group: Group = None,
133
+ **data: Any) -> VertexList | InstanceVertexList | IndexedVertexList | InstanceIndexedVertexList:
134
+ raise NotImplementedError
135
+
136
+ def vertex_list(self, count: int, mode: GeometryMode, batch: Batch = None, group: Group = None,
1491
137
  **data: Any) -> VertexList:
1492
138
  """Create a VertexList.
1493
139
 
@@ -1509,12 +155,12 @@ class ShaderProgram:
1509
155
  """
1510
156
  return self._vertex_list_create(count, mode, None, None, batch=batch, group=group, **data)
1511
157
 
1512
- def vertex_list_instanced(self, count: int, mode: int, instance_attributes: Sequence[str], batch: Batch = None,
1513
- group: Group = None, **data: Any) -> VertexList:
158
+ def vertex_list_instanced(self, count: int, mode: GeometryMode, instance_attributes: dict[str, int], batch: Batch = None,
159
+ group: Group = None, **data: Any) -> InstanceVertexList:
1514
160
  assert len(instance_attributes) > 0, "You must provide at least one attribute name to be instanced."
1515
161
  return self._vertex_list_create(count, mode, None, instance_attributes, batch=batch, group=group, **data)
1516
162
 
1517
- def vertex_list_indexed(self, count: int, mode: int, indices: Sequence[int], batch: Batch = None,
163
+ def vertex_list_indexed(self, count: int, mode: GeometryMode, indices: Sequence[int], batch: Batch = None,
1518
164
  group: Group = None, **data: Any) -> IndexedVertexList:
1519
165
  """Create a IndexedVertexList.
1520
166
 
@@ -1537,149 +183,217 @@ class ShaderProgram:
1537
183
  """
1538
184
  return self._vertex_list_create(count, mode, indices, None, batch=batch, group=group, **data)
1539
185
 
1540
- def vertex_list_instanced_indexed(self, count: int, mode: int, indices: Sequence[int],
186
+ def vertex_list_instanced_indexed(self, count: int, *, mode: GeometryMode, indices: Sequence[int],
1541
187
  instance_attributes: Sequence[str], batch: Batch = None, group: Group = None,
1542
- **data: Any) -> IndexedVertexList:
188
+ **data: Any) -> InstanceIndexedVertexList:
1543
189
  assert len(instance_attributes) > 0, "You must provide at least one attribute name to be instanced."
1544
190
  return self._vertex_list_create(count, mode, indices, instance_attributes, batch=batch, group=group, **data)
1545
191
 
1546
192
  def __repr__(self) -> str:
1547
193
  return f"{self.__class__.__name__}(id={self.id})"
1548
194
 
195
+ class ShaderSource(abc.ABC):
196
+ """String source of shader used during load of a Shader instance."""
1549
197
 
1550
- class ComputeShaderProgram:
1551
- """OpenGL Compute Shader Program."""
1552
- _context: Context | None
1553
- _id: int | None
1554
- _shader: Shader
1555
- _uniforms: dict[str, Any]
1556
- _uniform_blocks: dict[str, UniformBlock]
1557
- max_work_group_size: tuple[int, int, int]
1558
- max_work_group_count: tuple[int, int, int]
1559
- max_shared_memory_size: int
1560
- max_work_group_invocations: int
1561
-
1562
- def __init__(self, source: str) -> None:
1563
- """Create an OpenGL ComputeShaderProgram from source."""
1564
- self._id = None
198
+ @abstractmethod
199
+ def validate(self) -> str:
200
+ """Return the validated shader source."""
201
+
202
+
203
+ class ShaderBase(abc.ABC):
204
+ """Graphics shader.
205
+
206
+ Shader objects may be compiled on instantiation if OpenGL or already compiled in Vulkan.
207
+ You can reuse a Shader object in multiple ShaderPrograms.
208
+ """
209
+ _src_str: str
210
+ type: ShaderType
211
+
212
+ def __init__(self, source_string: str, shader_type: ShaderType) -> None:
213
+ """Initialize a shader type."""
214
+ self._src_str = source_string
215
+ self.type = shader_type
1565
216
 
1566
- if not (gl_info.have_version(4, 3) or gl_info.have_extension("GL_ARB_compute_shader")):
217
+ available_shaders = self.supported_shaders()
218
+ if shader_type not in available_shaders:
1567
219
  msg = (
1568
- "Compute Shader not supported. OpenGL Context version must be at least "
1569
- "4.3 or higher, or 4.2 with the 'GL_ARB_compute_shader' extension."
220
+ f"Shader type '{shader_type}' is not supported by this shader class."
221
+ f"Supported types are: {available_shaders}"
1570
222
  )
1571
223
  raise ShaderException(msg)
1572
224
 
1573
- self._shader = Shader(source, 'compute')
1574
- self._context = pyglet.gl.current_context
1575
- self._id = _link_program(self._shader)
225
+ @classmethod
226
+ @abstractmethod
227
+ def supported_shaders(cls: type[ShaderBase]) -> tuple[ShaderType, ...]:
228
+ """Return the supported shader types for this shader class."""
1576
229
 
1577
- if _debug_gl_shaders:
1578
- print(_get_program_log(self._id))
230
+ @staticmethod
231
+ @abstractmethod
232
+ def get_string_class() -> type[ShaderSource]:
233
+ """Return the proper ShaderSource class used to validate the shader."""
234
+
235
+ DataTypeTuple = ('?', 'f', 'i', 'I', 'h', 'H', 'b', 'B', 'q','Q')
236
+
237
+ _data_type_to_ctype = {
238
+ '?': ctypes.c_bool, # bool
239
+ 'b': ctypes.c_byte, # signed byte
240
+ 'B': ctypes.c_ubyte, # unsigned byte
241
+ 'h': ctypes.c_short, # signed short
242
+ 'H': ctypes.c_ushort, # unsigned short
243
+ 'i': ctypes.c_int, # signed int
244
+ 'I': ctypes.c_uint, # unsigned int
245
+ 'f': ctypes.c_float, # float
246
+ 'd': ctypes.c_double, # double
247
+ 'q': ctypes.c_longlong, # signed long long
248
+ 'Q': ctypes.c_ulonglong, # unsigned long long
249
+ }
1579
250
 
1580
- self._uniforms = _introspect_uniforms(self._id, True)
1581
- self._uniform_blocks = _introspect_uniform_blocks(self)
251
+ @dataclass(frozen=True)
252
+ class AttributeFormat:
253
+ """A format describing the properties of an Attribute."""
254
+ name: str
255
+ components: int # for example: 4 for vec4
256
+ data_type: DataTypes
257
+ normalized: bool
258
+ divisor: int # 0 = per-vertex, 1> = per-instance
1582
259
 
1583
- self.max_work_group_size = self._get_tuple(gl.GL_MAX_COMPUTE_WORK_GROUP_SIZE) # x, y, z
1584
- self.max_work_group_count = self._get_tuple(gl.GL_MAX_COMPUTE_WORK_GROUP_COUNT) # x, y, z
1585
- self.max_shared_memory_size = self._get_value(gl.GL_MAX_COMPUTE_SHARED_MEMORY_SIZE)
1586
- self.max_work_group_invocations = self._get_value(gl.GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS)
260
+ @property
261
+ def is_instanced(self) -> bool:
262
+ return self.divisor != 0
1587
263
 
1588
- @staticmethod
1589
- def _get_tuple(parameter: int) -> tuple[int, int, int]:
1590
- val_x = gl.GLint()
1591
- val_y = gl.GLint()
1592
- val_z = gl.GLint()
1593
- for i, value in enumerate((val_x, val_y, val_z)):
1594
- gl.glGetIntegeri_v(parameter, i, byref(value))
1595
- return val_x.value, val_y.value, val_z.value
264
+ @dataclass(frozen=True)
265
+ class AttributeView:
266
+ """Describes a view of the attribute at its bound buffer."""
267
+ offset: int # Offset start of element to this attribute
268
+ stride: int # Size from one element to the next
1596
269
 
1597
- @staticmethod
1598
- def _get_value(parameter: int) -> int:
1599
- val = gl.GLint()
1600
- gl.glGetIntegerv(parameter, byref(val))
1601
- return val.value
1602
270
 
1603
- @staticmethod
1604
- def dispatch(x: int = 1, y: int = 1, z: int = 1, barrier: int = gl.GL_ALL_BARRIER_BITS) -> None:
1605
- """Launch one or more compute work groups.
271
+ class Attribute:
272
+ """Describes an attribute in a shader."""
273
+ fmt: AttributeFormat
274
+ element_size: int
275
+ c_type: CType
276
+ location: int
277
+
278
+ def __init__(self, name: str, location: int, components: int, data_type: DataTypes, normalize: bool = False,
279
+ divisor: int = 0) -> None:
280
+ """Create the attribute accessor.
281
+
282
+ Args:
283
+ name:
284
+ Name of the vertex attribute.
285
+ location:
286
+ Location (index) of the vertex attribute.
287
+ components:
288
+ Number of components in the attribute.
289
+ data_type:
290
+ Data type intended for use with the attribute.
291
+ normalize:
292
+ True if OpenGL should normalize the values
293
+ divisor:
294
+ The divisor value if this is an instanced attribute.
1606
295
 
1607
- The ComputeShaderProgram should be active (bound) before calling
1608
- this method. The x, y, and z parameters specify the number of local
1609
- work groups that will be dispatched in the X, Y and Z dimensions.
1610
296
  """
1611
- glDispatchCompute(x, y, z)
1612
- if barrier:
1613
- glMemoryBarrier(barrier)
297
+ self.fmt = AttributeFormat(name, components, data_type, normalize, divisor)
298
+ self.location = location
1614
299
 
1615
- @property
1616
- def id(self) -> int:
1617
- return self._id
300
+ self.c_type = _data_type_to_ctype[self.fmt.data_type]
301
+ self.element_size = ctypes.sizeof(self.c_type)
1618
302
 
1619
- @property
1620
- def uniforms(self) -> dict[str, dict[str, Any]]:
1621
- return {n: {'location': u.location, 'length': u.length, 'size': u.size} for n, u in self._uniforms.items()}
303
+ def set_data_type(self, data_type: DataTypes, normalize: bool) -> None:
304
+ """Set datatype to a new format and normalization.
1622
305
 
1623
- @property
1624
- def uniform_blocks(self) -> dict[str, UniformBlock]:
1625
- return self._uniform_blocks
306
+ Must be done before this attribute is used, or may cause unexpected behavior.
307
+ """
308
+ self.fmt = AttributeFormat(self.fmt.name, self.fmt.components, data_type, normalize, self.fmt.divisor)
309
+ self.c_type = _data_type_to_ctype[self.fmt.data_type]
310
+ self.element_size = ctypes.sizeof(self.c_type)
1626
311
 
1627
- def use(self) -> None:
1628
- glUseProgram(self._id)
312
+ def set_divisor(self, divisor: int) -> None:
313
+ self.fmt = AttributeFormat(self.fmt.name, self.fmt.components, self.fmt.data_type, self.fmt.normalized, divisor)
1629
314
 
1630
- @staticmethod
1631
- def stop() -> None:
1632
- glUseProgram(0)
315
+ def __repr__(self) -> str:
316
+ return f"Attribute(location={self.location}, fmt={self.fmt}')"
1633
317
 
1634
- __enter__ = use
1635
- bind = use
1636
- unbind = stop
1637
318
 
1638
- def __exit__(self, *_) -> None: # noqa: ANN002
1639
- glUseProgram(0)
319
+ class GraphicsAttribute:
320
+ """A combination of format and view to give the overall attribute information."""
321
+ def __init__(self, attribute: Attribute, view: AttributeView) -> None:
322
+ self.attribute = attribute
323
+ self.view = view
1640
324
 
1641
- def delete(self) -> None:
1642
- glDeleteProgram(self._id)
1643
- self._id = None
325
+ def enable(self) -> None:
326
+ """Enable the attribute."""
327
+ raise NotImplementedError
328
+
329
+ def disable(self) -> None:
330
+ """Disable the attribute."""
331
+ raise NotImplementedError
1644
332
 
1645
- def __del__(self) -> None:
1646
- if self._id is not None:
1647
- try:
1648
- self._context.delete_shader_program(self._id)
1649
- self._id = None
1650
- except (AttributeError, ImportError):
1651
- pass # Interpreter is shutting down
1652
-
1653
- def __setitem__(self, key: str, value: Any) -> None:
1654
- try:
1655
- uniform = self._uniforms[key]
1656
- except KeyError as err:
1657
- msg = (f"A Uniform with the name `{key}` was not found.\n"
1658
- f"The spelling may be incorrect, or if not in use it "
1659
- f"may have been optimized out by the OpenGL driver.")
1660
- if _debug_gl_shaders:
1661
- warnings.warn(msg)
1662
- return
1663
-
1664
- raise ShaderException from err
1665
- try:
1666
- uniform.set(value)
1667
- except GLException as err:
1668
- raise ShaderException from err
1669
-
1670
- def __getitem__(self, item: str) -> Any:
1671
- try:
1672
- uniform = self._uniforms[item]
1673
- except KeyError as err:
1674
- msg = (f"A Uniform with the name `{item}` was not found.\n"
1675
- f"The spelling may be incorrect, or if not in use it "
1676
- f"may have been optimized out by the OpenGL driver.")
1677
- if _debug_gl_shaders:
1678
- warnings.warn(msg)
1679
- return None
1680
-
1681
- raise ShaderException(msg) from err
1682
- try:
1683
- return uniform.get()
1684
- except GLException as err:
1685
- raise ShaderException from err
333
+ def set_pointer(self) -> None:
334
+ """Setup this attribute to point to the currently bound buffer at the given offset."""
335
+ raise NotImplementedError
336
+
337
+ def set_divisor(self) -> None:
338
+ raise NotImplementedError
339
+
340
+
341
+ class UniformBufferObjectBase:
342
+ @abstractmethod
343
+ def read(self) -> bytes:
344
+ raise NotImplementedError
345
+
346
+
347
+ class UniformBlockBase:
348
+ program: CallableProxyType[Callable[..., Any] | Any] | Any
349
+ name: str
350
+ index: int
351
+ size: int
352
+ binding: int
353
+ uniforms: dict
354
+ view_cls: type[ctypes.Structure] | None
355
+ __slots__ = 'binding', 'index', 'name', 'program', 'size', 'uniform_count', 'uniforms', 'view_cls'
356
+
357
+ def __init__(self, program: ShaderProgramBase, name: str, index: int, size: int, binding: int,
358
+ uniforms: dict, uniform_count: int) -> None:
359
+ """Initialize a uniform block for a ShaderProgram."""
360
+ self.program = weakref.proxy(program)
361
+ self.name = name
362
+ self.index = index
363
+ self.size = size
364
+ self.binding = binding
365
+ self.uniforms = uniforms
366
+ self.uniform_count = uniform_count
367
+ self.view_cls = None
368
+
369
+ def bind(self, ubo: UniformBufferObjectBase) -> None:
370
+ """Bind the Uniform Buffer Object to the binding point of this Uniform Block."""
371
+ raise NotImplementedError
372
+
373
+ def create_ubo(self) -> UniformBufferObjectBase:
374
+ """Create a new UniformBufferObject from this uniform block."""
375
+ raise NotImplementedError
376
+
377
+ def set_binding(self, binding: int) -> None:
378
+ """Rebind the Uniform Block to a new binding index number.
379
+
380
+ This only affects the program this Uniform Block is derived from.
381
+
382
+ Binding value of 0 is reserved for the Pyglet's internal uniform block named ``WindowBlock``.
383
+
384
+ .. warning:: By setting a binding manually, the user is expected to manage all Uniform Block bindings
385
+ for all shader programs manually. Since the internal global ID's will be unaware of changes set
386
+ by this function, collisions may occur if you use a lower number.
387
+
388
+ .. note:: You must call ``create_ubo`` to get another Uniform Buffer Object after calling this,
389
+ as the previous buffers are still bound to the old binding point.
390
+ """
391
+ raise NotImplementedError
392
+
393
+ def _introspect_uniforms(self) -> type[ctypes.Structure]:
394
+ """Introspect the block's structure and return a ctypes struct for manipulating the uniform block's members."""
395
+ raise NotImplementedError
396
+
397
+ def __repr__(self) -> str:
398
+ return (f"{self.__class__.__name__}(program={self.program.id}, location={self.index}, size={self.size}, "
399
+ f"binding={self.binding})")