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