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 }