1 module gfx.gl3;
2
3 import gfx.core.log : LogTag;
4 import gfx.core.rc : AtomicRefCounted;
5 import gfx.graal : DebugCallback, Instance;
6 import gfx.graal.device : PhysicalDevice;
7
8 enum gfxGlLogMask = 0x2000_0000;
9 package immutable gfxGlLog = LogTag("GFX-GL", gfxGlLogMask);
10
11 final class GlInstance : Instance
12 {
13 import gfx.core.rc : atomicRcCode, Rc;
14 import gfx.gl3.context : GlContext;
15 import gfx.graal : ApiProps, Backend;
16
17 mixin(atomicRcCode);
18
19 private Rc!GlContext _ctx;
20 private Rc!GlShare _share;
21 private PhysicalDevice _phd;
22
23 this(GlContext ctx) {
24 _ctx = ctx;
25 _share = new GlShare(_ctx);
26 _phd = new GlPhysicalDevice(this);
27 }
28
29 override void dispose() {
30 _share.unload();
31 _ctx.unload();
32 }
33
34 override @property Backend backend() {
35 return Backend.gl3;
36 }
37
38 override @property ApiProps apiProps()
39 {
40 import gfx.math.proj : ndc, XYClip, ZClip;
41
42 return ApiProps(
43 "gl3", ndc(XYClip.leftHand, ZClip.minusOneToOne)
44 );
45 }
46
47 override PhysicalDevice[] devices() {
48 return [ _phd ];
49 }
50
51 override void setDebugCallback(DebugCallback callback) {
52 _share._callback = callback;
53 }
54
55 @property GlShare share() {
56 return _share.obj;
57 }
58 @property GlContext ctx() {
59 return _ctx.obj;
60 }
61 }
62
63 package:
64
65 import gfx.bindings.opengl.gl : Gl;
66
67 struct GlInfo
68 {
69 ushort glVer;
70 ushort glslVer;
71 bool bufferStorage;
72 bool textureStorage;
73 bool textureStorageMS;
74 bool samplerObject;
75 bool drawElementsBaseVertex;
76 bool baseInstance;
77 bool viewportArray;
78 bool polygonOffsetClamp;
79
80 private static GlInfo fetchAndCheck (Gl gl, uint glVer) {
81 import gfx.bindings.opengl.gl : GL_VERSION, GL_SHADING_LANGUAGE_VERSION;
82 import gfx.gl3.context : glAvailableExtensions, glRequiredExtensions, glslVersion;
83 import std.algorithm : canFind;
84 import std..string : fromStringz;
85
86 const exts = glAvailableExtensions(gl);
87
88 foreach (glE; glRequiredExtensions) {
89 import std.exception : enforce;
90 import std.format : format;
91 enforce(exts.canFind(glE), format(glE ~ " is required but was not found"));
92 }
93
94 bool checkVersion(uint ver) {
95 return glVer >= ver;
96 }
97
98 bool checkFeature(uint ver, in string ext) {
99 return glVer >= ver || exts.canFind(ext);
100 }
101
102 GlInfo gi;
103 gi.glVer = cast(ushort)glVer;
104 gi.glslVer = cast(ushort)glslVersion(glVer);
105 gi.bufferStorage = checkFeature(44, "GL_ARB_buffer_storage");
106 gi.textureStorage = checkFeature(42, "GL_ARB_texture_storage");
107 gi.textureStorageMS = checkFeature(43, "GL_ARB_texture_storage_multisample");
108 gi.samplerObject = checkFeature(33, "GL_ARB_sampler_objects");
109 gi.drawElementsBaseVertex = checkFeature(32, "ARB_draw_elements_base_vertex");
110 gi.baseInstance = checkFeature(42, "ARB_base_instance");
111 gi.viewportArray = checkFeature(41, "ARB_viewport_array");
112 gi.polygonOffsetClamp = exts.canFind("GL_EXT_polygon_offset_clamp");
113 return gi;
114 }
115 }
116
117
118 final class GlShare : AtomicRefCounted
119 {
120 import gfx.core.rc : Rc;
121 import gfx.gl3.context : GlContext;
122
123 private Rc!GlContext _ctx;
124 private Gl _gl;
125 private GlInfo _info;
126 private DebugCallback _callback;
127
128 this(GlContext ctx)
129 {
130 import std.exception : enforce;
131
132 _ctx = ctx;
133 enforce(_ctx.current, "The OpenGL context must be made current before initializing instance");
134 _gl = ctx.gl;
135 _info = GlInfo.fetchAndCheck(_gl, ctx.attribs.decimalVersion);
136 }
137
138 override void dispose() {
139 _ctx.unload();
140 }
141
142 @property inout(GlContext) ctx() inout {
143 return _ctx.obj;
144 }
145 @property inout(Gl) gl() inout {
146 return _gl;
147 }
148 @property GlInfo info() const {
149 return _info;
150 }
151 }
152
153
154 final class GlPhysicalDevice : PhysicalDevice
155 {
156 import gfx.bindings.opengl.gl : Gl;
157 import gfx.gl3.context : GlContext;
158 import gfx.graal.device : Device, DeviceFeatures, DeviceLimits, DeviceType,
159 QueueRequest;
160 import gfx.graal.format : Format, FormatProperties;
161 import gfx.graal.memory : MemoryProperties;
162 import gfx.graal.queue : QueueFamily;
163 import gfx.graal.presentation : PresentMode, Surface, SurfaceCaps;
164
165 private GlInstance _inst;
166 private string _name;
167
168 this(GlInstance instance) {
169 _inst = instance;
170
171 import gfx.bindings.opengl.gl : GL_RENDERER;
172 import std..string : fromStringz;
173 _name = fromStringz(cast(const(char)*)_inst.share.gl.GetString(GL_RENDERER)).idup;
174 }
175
176 override @property Instance instance()
177 {
178 import gfx.core.rc : lockObj, giveAwayObj;
179
180 auto inst = lockObj(_inst);
181 if (!inst) return null;
182 return giveAwayObj(inst);
183 }
184
185 override @property string name() {
186 return _name;
187 }
188
189 override @property DeviceType type() {
190 return DeviceType.other;
191 }
192
193 override @property DeviceFeatures features() {
194 // TODO
195 return DeviceFeatures.all();
196 }
197
198 override @property DeviceLimits limits() {
199 // TODO: Query values with `glGet`
200 return DeviceLimits.init;
201 }
202
203 override @property MemoryProperties memoryProperties() {
204 import gfx.graal.memory : MemoryHeap, MemProps, MemoryType;
205 const props = MemProps.deviceLocal | MemProps.hostVisible | MemProps.hostCoherent;
206 // TODO: buffer storage
207 return MemoryProperties(
208 [ MemoryHeap(size_t.max, true) ],
209 [ MemoryType(props, 0) ]
210 );
211 }
212
213 override @property QueueFamily[] queueFamilies() {
214 import gfx.graal.queue : QueueCap;
215 return [
216 QueueFamily(QueueCap.graphics, 1)
217 ];
218 }
219
220 override FormatProperties formatProperties(in Format format) {
221 import gfx.graal.format : FormatFeatures;
222
223 const color = FormatFeatures.sampledImage |
224 FormatFeatures.colorAttachment | FormatFeatures.blit;
225 const depthStencil = FormatFeatures.depthStencilAttachment;
226
227 switch (format) {
228 case Format.r8_uNorm:
229 case Format.rg8_uNorm:
230 case Format.rgba8_uNorm:
231 case Format.r8_sInt:
232 case Format.rg8_sInt:
233 case Format.rgba8_sInt:
234 case Format.r8_uInt:
235 case Format.rg8_uInt:
236 case Format.rgba8_uInt:
237 case Format.r16_uNorm:
238 case Format.rg16_uNorm:
239 case Format.rgba16_uNorm:
240 case Format.r16_sInt:
241 case Format.rg16_sInt:
242 case Format.rgba16_sInt:
243 case Format.r16_uInt:
244 case Format.rg16_uInt:
245 case Format.rgba16_uInt:
246 return FormatProperties(
247 color, color, FormatFeatures.init
248 );
249 case Format.d16_uNorm:
250 case Format.x8d24_uNorm:
251 case Format.d32_sFloat:
252 case Format.d24s8_uNorm:
253 case Format.s8_uInt:
254 return FormatProperties(
255 depthStencil, depthStencil, FormatFeatures.init
256 );
257 default:
258 return FormatProperties.init;
259 }
260 }
261
262 override bool supportsSurface(uint queueFamilyIndex, Surface surface) {
263 return true;
264 }
265
266 override SurfaceCaps surfaceCaps(Surface surface) {
267 import gfx.graal.image : ImageUsage;
268 import gfx.graal.presentation : CompositeAlpha;
269 return SurfaceCaps(
270 2, 4, [4, 4], [16384, 16384], 1,
271 ImageUsage.colorAttachment | ImageUsage.sampled | ImageUsage.transfer,
272 CompositeAlpha.inherit
273 );
274 }
275
276 override Format[] surfaceFormats(Surface surface) {
277 return [ Format.rgba8_uNorm ];
278 }
279
280 override PresentMode[] surfacePresentModes(Surface surface) {
281 return [ PresentMode.fifo ];
282 }
283
284 /// Open a logical device with the specified queues.
285 /// Returns: null if it can't meet all requested queues, the opened device otherwise.
286 override Device open(in QueueRequest[], in DeviceFeatures)
287 {
288 import gfx.core.rc : lockObj, releaseObj;
289 import gfx.gl3.device : GlDevice;
290 auto inst = lockObj(_inst);
291 scope(exit) releaseObj(inst);
292 if (!inst) return null;
293 else return new GlDevice(this, inst);
294 }
295 }