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