pyglet 2.1.12__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.
- pyglet/__init__.py +67 -61
- pyglet/__init__.pyi +15 -8
- pyglet/app/__init__.py +22 -13
- pyglet/app/async_app.py +212 -0
- pyglet/app/base.py +2 -1
- pyglet/app/{xlib.py → linux.py} +3 -3
- pyglet/config/__init__.py +101 -0
- pyglet/config/gl/__init__.py +30 -0
- pyglet/config/gl/egl.py +120 -0
- pyglet/config/gl/macos.py +262 -0
- pyglet/config/gl/windows.py +267 -0
- pyglet/config/gl/x11.py +142 -0
- pyglet/customtypes.py +43 -2
- pyglet/display/__init__.py +8 -6
- pyglet/display/base.py +3 -63
- pyglet/display/cocoa.py +12 -17
- pyglet/display/emscripten.py +39 -0
- pyglet/display/headless.py +23 -30
- pyglet/display/wayland.py +157 -0
- pyglet/display/win32.py +4 -17
- pyglet/display/xlib.py +19 -27
- pyglet/display/xlib_vidmoderestore.py +2 -2
- pyglet/enums.py +183 -0
- pyglet/event.py +0 -1
- pyglet/experimental/geoshader_sprite.py +15 -13
- pyglet/experimental/hidraw.py +6 -15
- pyglet/experimental/multitexture_sprite.py +31 -19
- pyglet/experimental/particles.py +13 -35
- pyglet/font/__init__.py +251 -85
- pyglet/font/base.py +116 -61
- pyglet/font/dwrite/__init__.py +349 -204
- pyglet/font/dwrite/dwrite_lib.py +27 -5
- pyglet/font/fontconfig.py +14 -6
- pyglet/font/freetype.py +138 -87
- pyglet/font/freetype_lib.py +19 -0
- pyglet/font/group.py +179 -0
- pyglet/font/harfbuzz/__init__.py +3 -3
- pyglet/font/pyodide_js.py +310 -0
- pyglet/font/quartz.py +319 -126
- pyglet/font/ttf.py +45 -3
- pyglet/font/user.py +14 -19
- pyglet/font/win32.py +45 -21
- pyglet/graphics/__init__.py +8 -787
- pyglet/graphics/allocation.py +115 -1
- pyglet/graphics/api/__init__.py +77 -0
- pyglet/graphics/api/base.py +299 -0
- pyglet/graphics/api/gl/__init__.py +58 -0
- pyglet/graphics/api/gl/base.py +24 -0
- pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
- pyglet/graphics/api/gl/cocoa/context.py +76 -0
- pyglet/graphics/api/gl/context.py +391 -0
- pyglet/graphics/api/gl/default_shaders.py +0 -0
- pyglet/graphics/api/gl/draw.py +627 -0
- pyglet/graphics/api/gl/egl/__init__.py +0 -0
- pyglet/graphics/api/gl/egl/context.py +92 -0
- pyglet/graphics/api/gl/enums.py +76 -0
- pyglet/graphics/api/gl/framebuffer.py +315 -0
- pyglet/graphics/api/gl/gl.py +5463 -0
- pyglet/graphics/api/gl/gl_info.py +188 -0
- pyglet/graphics/api/gl/global_opengl.py +226 -0
- pyglet/{gl → graphics/api/gl}/lib.py +34 -18
- pyglet/graphics/api/gl/shader.py +1476 -0
- pyglet/graphics/api/gl/shapes.py +55 -0
- pyglet/graphics/api/gl/sprite.py +102 -0
- pyglet/graphics/api/gl/state.py +219 -0
- pyglet/graphics/api/gl/text.py +190 -0
- pyglet/graphics/api/gl/texture.py +1526 -0
- pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
- pyglet/graphics/api/gl/vertexdomain.py +751 -0
- pyglet/graphics/api/gl/win32/__init__.py +0 -0
- pyglet/graphics/api/gl/win32/context.py +108 -0
- pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
- pyglet/graphics/api/gl/xlib/__init__.py +0 -0
- pyglet/graphics/api/gl/xlib/context.py +174 -0
- pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
- pyglet/graphics/api/gl1/__init__.py +0 -0
- pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
- pyglet/graphics/api/gl2/__init__.py +0 -0
- pyglet/graphics/api/gl2/buffer.py +320 -0
- pyglet/graphics/api/gl2/draw.py +600 -0
- pyglet/graphics/api/gl2/global_opengl.py +122 -0
- pyglet/graphics/api/gl2/shader.py +200 -0
- pyglet/graphics/api/gl2/shapes.py +51 -0
- pyglet/graphics/api/gl2/sprite.py +79 -0
- pyglet/graphics/api/gl2/text.py +175 -0
- pyglet/graphics/api/gl2/vertexdomain.py +364 -0
- pyglet/graphics/api/webgl/__init__.py +233 -0
- pyglet/graphics/api/webgl/buffer.py +302 -0
- pyglet/graphics/api/webgl/context.py +234 -0
- pyglet/graphics/api/webgl/draw.py +590 -0
- pyglet/graphics/api/webgl/enums.py +76 -0
- pyglet/graphics/api/webgl/framebuffer.py +360 -0
- pyglet/graphics/api/webgl/gl.py +1537 -0
- pyglet/graphics/api/webgl/gl_info.py +130 -0
- pyglet/graphics/api/webgl/shader.py +1346 -0
- pyglet/graphics/api/webgl/shapes.py +92 -0
- pyglet/graphics/api/webgl/sprite.py +102 -0
- pyglet/graphics/api/webgl/state.py +227 -0
- pyglet/graphics/api/webgl/text.py +187 -0
- pyglet/graphics/api/webgl/texture.py +1227 -0
- pyglet/graphics/api/webgl/vertexarray.py +54 -0
- pyglet/graphics/api/webgl/vertexdomain.py +616 -0
- pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
- pyglet/{image → graphics}/atlas.py +33 -32
- pyglet/graphics/base.py +10 -0
- pyglet/graphics/buffer.py +245 -0
- pyglet/graphics/draw.py +578 -0
- pyglet/graphics/framebuffer.py +26 -0
- pyglet/graphics/instance.py +178 -69
- pyglet/graphics/shader.py +267 -1553
- pyglet/graphics/state.py +83 -0
- pyglet/graphics/texture.py +703 -0
- pyglet/graphics/vertexdomain.py +695 -538
- pyglet/gui/ninepatch.py +10 -10
- pyglet/gui/widgets.py +120 -10
- pyglet/image/__init__.py +20 -1973
- pyglet/image/animation.py +12 -12
- pyglet/image/base.py +730 -0
- pyglet/image/codecs/__init__.py +9 -0
- pyglet/image/codecs/bmp.py +53 -30
- pyglet/image/codecs/dds.py +53 -31
- pyglet/image/codecs/gdiplus.py +38 -14
- pyglet/image/codecs/gdkpixbuf2.py +0 -2
- pyglet/image/codecs/js_image.py +99 -0
- pyglet/image/codecs/ktx2.py +161 -0
- pyglet/image/codecs/pil.py +1 -1
- pyglet/image/codecs/png.py +1 -1
- pyglet/image/codecs/wic.py +11 -2
- pyglet/info.py +26 -24
- pyglet/input/__init__.py +8 -0
- pyglet/input/base.py +163 -105
- pyglet/input/controller.py +13 -19
- pyglet/input/controller_db.py +39 -24
- pyglet/input/emscripten/__init__.py +18 -0
- pyglet/input/emscripten/gamepad_js.py +397 -0
- pyglet/input/linux/__init__.py +11 -5
- pyglet/input/linux/evdev.py +10 -11
- pyglet/input/linux/x11_xinput.py +2 -2
- pyglet/input/linux/x11_xinput_tablet.py +1 -1
- pyglet/input/macos/__init__.py +7 -2
- pyglet/input/macos/darwin_gc.py +559 -0
- pyglet/input/win32/__init__.py +1 -1
- pyglet/input/win32/directinput.py +34 -29
- pyglet/input/win32/xinput.py +11 -61
- pyglet/lib.py +3 -3
- pyglet/libs/__init__.py +1 -1
- pyglet/{gl → libs/darwin}/agl.py +1 -1
- pyglet/libs/darwin/cocoapy/__init__.py +2 -2
- pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
- pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
- pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
- pyglet/libs/darwin/cocoapy/runtime.py +81 -45
- pyglet/libs/darwin/coreaudio.py +4 -4
- pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
- pyglet/libs/darwin/quartzkey.py +1 -3
- pyglet/libs/egl/__init__.py +2 -0
- pyglet/libs/egl/egl_lib.py +576 -0
- pyglet/libs/egl/eglext.py +51 -5
- pyglet/libs/linux/__init__.py +0 -0
- pyglet/libs/linux/egl/__init__.py +0 -0
- pyglet/libs/linux/egl/eglext.py +22 -0
- pyglet/libs/linux/glx/__init__.py +0 -0
- pyglet/{gl → libs/linux/glx}/glx.py +13 -14
- pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
- pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
- pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
- pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
- pyglet/libs/linux/wayland/__init__.py +0 -0
- pyglet/libs/linux/wayland/client.py +1068 -0
- pyglet/libs/linux/wayland/lib_wayland.py +207 -0
- pyglet/libs/linux/wayland/wayland_egl.py +38 -0
- pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
- pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
- pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
- pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
- pyglet/libs/linux/x11/xrandr.py +0 -0
- pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
- pyglet/libs/shared/__init__.py +0 -0
- pyglet/libs/shared/spirv/__init__.py +0 -0
- pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
- pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
- pyglet/libs/win32/__init__.py +27 -5
- pyglet/libs/win32/constants.py +59 -48
- pyglet/libs/win32/context_managers.py +20 -3
- pyglet/libs/win32/dinput.py +105 -88
- pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
- pyglet/libs/win32/types.py +58 -23
- pyglet/{gl → libs/win32}/wgl.py +32 -25
- pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
- pyglet/media/__init__.py +9 -10
- pyglet/media/codecs/__init__.py +12 -1
- pyglet/media/codecs/base.py +99 -96
- pyglet/media/codecs/ffmpeg.py +2 -2
- pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
- pyglet/media/codecs/webaudio_pyodide.py +111 -0
- pyglet/media/drivers/__init__.py +9 -4
- pyglet/media/drivers/base.py +4 -4
- pyglet/media/drivers/openal/__init__.py +1 -1
- pyglet/media/drivers/openal/adaptation.py +3 -3
- pyglet/media/drivers/pulse/__init__.py +1 -1
- pyglet/media/drivers/pulse/adaptation.py +3 -3
- pyglet/media/drivers/pyodide_js/__init__.py +8 -0
- pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
- pyglet/media/drivers/xaudio2/adaptation.py +3 -3
- pyglet/media/player.py +276 -193
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/model/__init__.py +39 -29
- pyglet/model/codecs/base.py +4 -4
- pyglet/model/codecs/gltf.py +3 -3
- pyglet/model/codecs/obj.py +71 -43
- pyglet/resource.py +129 -78
- pyglet/shapes.py +147 -177
- pyglet/sprite.py +47 -164
- pyglet/text/__init__.py +44 -54
- pyglet/text/caret.py +12 -7
- pyglet/text/document.py +19 -17
- pyglet/text/formats/html.py +2 -2
- pyglet/text/formats/structured.py +10 -40
- pyglet/text/layout/__init__.py +20 -13
- pyglet/text/layout/base.py +176 -287
- pyglet/text/layout/incremental.py +9 -10
- pyglet/text/layout/scrolling.py +7 -95
- pyglet/window/__init__.py +183 -172
- pyglet/window/cocoa/__init__.py +62 -51
- pyglet/window/cocoa/pyglet_delegate.py +2 -25
- pyglet/window/cocoa/pyglet_view.py +9 -8
- pyglet/window/dialog/__init__.py +184 -0
- pyglet/window/dialog/base.py +99 -0
- pyglet/window/dialog/darwin.py +121 -0
- pyglet/window/dialog/linux.py +72 -0
- pyglet/window/dialog/windows.py +194 -0
- pyglet/window/emscripten/__init__.py +779 -0
- pyglet/window/headless/__init__.py +44 -28
- pyglet/window/key.py +2 -0
- pyglet/window/mouse.py +2 -2
- pyglet/window/wayland/__init__.py +377 -0
- pyglet/window/win32/__init__.py +101 -46
- pyglet/window/xlib/__init__.py +104 -66
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
- pyglet-3.0.dev1.dist-info/RECORD +322 -0
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
- pyglet/gl/__init__.py +0 -208
- pyglet/gl/base.py +0 -499
- pyglet/gl/cocoa.py +0 -309
- pyglet/gl/gl.py +0 -4625
- pyglet/gl/gl.pyi +0 -2320
- pyglet/gl/gl_compat.pyi +0 -3097
- pyglet/gl/gl_info.py +0 -190
- pyglet/gl/headless.py +0 -166
- pyglet/gl/wgl_info.py +0 -36
- pyglet/gl/wglext_nv.py +0 -1096
- pyglet/gl/win32.py +0 -268
- pyglet/gl/xlib.py +0 -295
- pyglet/image/buffer.py +0 -274
- pyglet/image/codecs/s3tc.py +0 -354
- pyglet/libs/x11/xrandr.py +0 -166
- pyglet-2.1.12.dist-info/RECORD +0 -234
- /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
- /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
- /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
- /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
- /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
- {pyglet-2.1.12.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
|
|
4
|
-
import
|
|
3
|
+
import abc
|
|
4
|
+
import ctypes
|
|
5
5
|
import weakref
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from
|
|
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
|
-
|
|
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
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
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
|
-
|
|
705
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1253
|
-
|
|
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
|
-
|
|
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)
|
|
79
|
+
def id(self):
|
|
1265
80
|
return self._id
|
|
1266
81
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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
|
-
|
|
88
|
+
def set_attributes(self, *attributes: Attribute) -> None:
|
|
89
|
+
"""Define the attributes of the vertex shader.
|
|
1277
90
|
|
|
1278
|
-
|
|
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
|
-
|
|
1293
|
-
|
|
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
|
|
1306
|
-
|
|
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
|
-
|
|
1310
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1442
|
-
|
|
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:
|
|
1513
|
-
group: Group = None, **data: Any) ->
|
|
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:
|
|
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:
|
|
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) ->
|
|
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
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
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
|
-
|
|
217
|
+
available_shaders = self.supported_shaders()
|
|
218
|
+
if shader_type not in available_shaders:
|
|
1567
219
|
msg = (
|
|
1568
|
-
"
|
|
1569
|
-
"
|
|
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
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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
|
-
|
|
1578
|
-
|
|
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
|
-
|
|
1581
|
-
|
|
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
|
-
|
|
1584
|
-
|
|
1585
|
-
self.
|
|
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
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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
|
-
|
|
1612
|
-
|
|
1613
|
-
glMemoryBarrier(barrier)
|
|
297
|
+
self.fmt = AttributeFormat(name, components, data_type, normalize, divisor)
|
|
298
|
+
self.location = location
|
|
1614
299
|
|
|
1615
|
-
|
|
1616
|
-
|
|
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
|
-
|
|
1620
|
-
|
|
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
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
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
|
|
1628
|
-
|
|
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
|
-
|
|
1631
|
-
|
|
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
|
-
|
|
1639
|
-
|
|
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
|
|
1642
|
-
|
|
1643
|
-
|
|
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
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
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})")
|