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 }