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 }