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