1 module example; 2 3 import gfx.core.rc; 4 import gfx.graal; 5 import gfx.graal.cmd; 6 import gfx.graal.device; 7 import gfx.graal.queue; 8 import gfx.graal.sync; 9 import gfx.vulkan; 10 import gfx.window; 11 12 import std.algorithm; 13 import std.exception; 14 import std.typecons; 15 16 class Example : Disposable 17 { 18 string title; 19 Rc!Instance instance; 20 Window window; 21 uint graphicsQueueIndex; 22 uint presentQueueIndex; 23 Rc!PhysicalDevice physicalDevice; 24 Rc!Device device; 25 Queue graphicsQueue; 26 Queue presentQueue; 27 uint[2] surfaceSize; 28 bool hasAlpha; 29 Rc!Swapchain swapchain; 30 Image[] scImages; 31 Rc!Semaphore imageAvailableSem; 32 Rc!Semaphore renderingFinishSem; 33 Rc!CommandPool cmdPool; 34 CommandBuffer[] cmdBufs; 35 Fence[] fences; 36 37 enum numCmdBufs=2; 38 size_t cmdBufInd; 39 40 this (string title) 41 { 42 this.title = title; 43 } 44 45 override void dispose() { 46 if (device) { 47 device.waitIdle(); 48 } 49 releaseArray(fences); 50 if (cmdPool && cmdBufs.length) { 51 cmdPool.free(cmdBufs); 52 cmdPool.unload(); 53 } 54 // the rest is checked with Rc, so it is safe to call unload even 55 // if object is invalid 56 imageAvailableSem.unload(); 57 renderingFinishSem.unload(); 58 swapchain.unload(); 59 device.unload(); 60 physicalDevice.unload(); 61 if (window) { 62 window.close(); 63 } 64 instance.unload(); 65 } 66 67 void prepare() 68 { 69 import std.format : format; 70 // initialize vulkan library 71 vulkanInit(); 72 // create a vulkan instance 73 instance = createVulkanInstance( 74 format("Gfx-d %s Example", title), 75 VulkanVersion(0, 0, 1) 76 ).rc; 77 // create a window for the running platform 78 // the window surface is created during this process 79 window = createWindow(instance); 80 81 // the rest of the preparation 82 prepareDevice(); 83 prepareSwapchain(null); 84 prepareSync(); 85 prepareCmds(); 86 } 87 88 void prepareDevice() 89 { 90 bool checkDevice(PhysicalDevice dev) { 91 graphicsQueueIndex = uint.max; 92 presentQueueIndex = uint.max; 93 if (dev.softwareRendering) return false; 94 foreach (uint i, qf; dev.queueFamilies) { 95 const graphics = qf.cap & QueueCap.graphics; 96 const present = dev.supportsSurface(i, window.surface); 97 if (graphics && present) { 98 graphicsQueueIndex = i; 99 presentQueueIndex = i; 100 return true; 101 } 102 if (graphics) graphicsQueueIndex = i; 103 if (present) presentQueueIndex = i; 104 } 105 return graphicsQueueIndex != uint.max && presentQueueIndex != uint.max; 106 } 107 foreach (pd; instance.devices) { 108 if (checkDevice(pd)) { 109 auto qrs = [ QueueRequest(graphicsQueueIndex, [ 0.5f ]) ]; 110 if (graphicsQueueIndex != presentQueueIndex) { 111 qrs ~= QueueRequest(presentQueueIndex, [ 0.5f ]); 112 } 113 physicalDevice = pd; 114 device = pd.open(qrs); 115 graphicsQueue = device.getQueue(graphicsQueueIndex, 0); 116 presentQueue = device.getQueue(presentQueueIndex, 0); 117 break; 118 } 119 } 120 } 121 122 void prepareSwapchain(Swapchain former=null) { 123 const surfCaps = physicalDevice.surfaceCaps(window.surface); 124 enforce(surfCaps.usage & ImageUsage.transferDst, "TransferDst not supported by surface"); 125 const usage = ImageUsage.transferDst | ImageUsage.colorAttachment; 126 const numImages = max(2, surfCaps.minImages); 127 enforce(surfCaps.maxImages == 0 || surfCaps.maxImages >= numImages); 128 const f = chooseFormat(physicalDevice, window.surface); 129 hasAlpha = (surfCaps.supportedAlpha & CompositeAlpha.preMultiplied) == CompositeAlpha.preMultiplied; 130 const ca = hasAlpha ? CompositeAlpha.preMultiplied : CompositeAlpha.opaque; 131 surfaceSize = [ 640, 480 ]; 132 foreach (i; 0..2) { 133 surfaceSize[i] = clamp(surfaceSize[i], surfCaps.minSize[i], surfCaps.maxSize[i]); 134 } 135 const pm = choosePresentMode(physicalDevice, window.surface); 136 137 swapchain = device.createSwapchain(window.surface, pm, numImages, f, surfaceSize, usage, ca, former); 138 scImages = swapchain.images; 139 } 140 141 void prepareSync() { 142 imageAvailableSem = device.createSemaphore(); 143 renderingFinishSem = device.createSemaphore(); 144 fences = new Fence[numCmdBufs]; 145 foreach (i; 0 .. numCmdBufs) { 146 fences[i] = device.createFence(Yes.signaled); 147 } 148 retainArray(fences); 149 } 150 151 void prepareCmds() { 152 cmdPool = device.createCommandPool(graphicsQueueIndex); 153 cmdBufs = cmdPool.allocate(numCmdBufs); 154 } 155 156 abstract void recordCmds(size_t cmdBufInd, size_t imgInd); 157 158 size_t nextCmdBuf() { 159 const ind = cmdBufInd++; 160 if (cmdBufInd == numCmdBufs) { 161 cmdBufInd = 0; 162 } 163 return ind; 164 } 165 166 void render() 167 { 168 import core.time : dur; 169 170 bool needReconstruction; 171 const imgInd = swapchain.acquireNextImage(dur!"seconds"(-1), imageAvailableSem, needReconstruction); 172 const cmdBufInd = nextCmdBuf(); 173 174 fences[cmdBufInd].wait(); 175 fences[cmdBufInd].reset(); 176 177 recordCmds(cmdBufInd, imgInd); 178 179 presentQueue.submit([ 180 Submission ( 181 [ StageWait(imageAvailableSem, PipelineStage.transfer) ], 182 [ renderingFinishSem ], [ cmdBufs[cmdBufInd] ] 183 ) 184 ], fences[cmdBufInd] ); 185 186 presentQueue.present( 187 [ renderingFinishSem ], 188 [ PresentRequest(swapchain, imgInd) ] 189 ); 190 191 // if (needReconstruction) { 192 // prepareSwapchain(swapchain); 193 // presentPool.reset(); 194 // } 195 } 196 } 197 198 /// Return a format suitable for the surface. 199 /// - if supported by the surface Format.rgba8_uNorm 200 /// - otherwise the first format with uNorm numeric format 201 /// - otherwise the first format 202 Format chooseFormat(PhysicalDevice pd, Surface surface) 203 { 204 const formats = pd.surfaceFormats(surface); 205 enforce(formats.length, "Could not get surface formats"); 206 if (formats.length == 1 && formats[0] == Format.undefined) { 207 return Format.rgba8_uNorm; 208 } 209 foreach(f; formats) { 210 if (f == Format.rgba8_uNorm) { 211 return f; 212 } 213 } 214 foreach(f; formats) { 215 if (f.formatDesc.numFormat == NumFormat.uNorm) { 216 return f; 217 } 218 } 219 return formats[0]; 220 } 221 222 PresentMode choosePresentMode(PhysicalDevice pd, Surface surface) 223 { 224 // auto modes = pd.surfacePresentModes(surface); 225 // if (modes.canFind(PresentMode.mailbox)) { 226 // return PresentMode.mailbox; 227 // } 228 assert(pd.surfacePresentModes(surface).canFind(PresentMode.fifo)); 229 return PresentMode.fifo; 230 }