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 }