1 module stencil; 2 3 import example; 4 5 import gfx.core; 6 import gfx.graal; 7 import gfx.window; 8 9 import std.exception; 10 import std.stdio; 11 import std.typecons; 12 13 class StencilExample : Example 14 { 15 Rc!RenderPass renderPass; 16 Rc!Pipeline stencilWritePipeline; 17 Rc!DescriptorSetLayout stencilWriteDSL; 18 Rc!PipelineLayout stencilWriteLayout; 19 DescriptorSet descriptorSet; 20 Rc!DescriptorPool descPool; 21 22 Rc!Pipeline solidPipeline; 23 24 Rc!Buffer vertBuf; 25 Rc!Buffer indBuf; 26 Rc!Image chessboard; 27 Rc!ImageView chessboardView; 28 Rc!Sampler sampler; 29 30 struct VertexP2T2 { 31 float[2] position; 32 float[2] texCoord; 33 } 34 immutable square = [ 35 VertexP2T2([-1.0, 1.0], [0.0, 1.0]), 36 VertexP2T2([-1.0, -1.0], [0.0, 0.0]), 37 VertexP2T2([ 1.0, -1.0], [1.0, 0.0]), 38 VertexP2T2([ 1.0, 1.0], [1.0, 1.0]), 39 ]; 40 immutable squareLen = square.length * VertexP2T2.sizeof; 41 immutable ushort[] squareIndices = [ 42 0, 1, 2, 0, 2, 3 43 ]; 44 45 46 struct VertexP2C3 { 47 float[2] position; 48 float[3] color; 49 } 50 immutable triangle = [ 51 VertexP2C3([-1.0, 1.0], [1.0, 0.0, 0.0]), 52 VertexP2C3([ 1.0, 1.0], [0.0, 1.0, 0.0]), 53 VertexP2C3([ 0.0, -1.0], [0.0, 0.0, 1.0]), 54 ]; 55 immutable triangleLen = triangle.length * VertexP2C3.sizeof; 56 57 this(string[] args) { 58 super("Stencil", args); 59 } 60 61 override void dispose() { 62 if (device) { 63 device.waitIdle(); 64 } 65 renderPass.unload(); 66 stencilWritePipeline.unload(); 67 stencilWriteDSL.unload(); 68 stencilWriteLayout.unload(); 69 descPool.unload(); 70 71 solidPipeline.unload(); 72 73 vertBuf.unload(); 74 indBuf.unload(); 75 chessboard.unload(); 76 chessboardView.unload(); 77 sampler.unload(); 78 79 super.dispose(); 80 } 81 82 override void prepare() 83 { 84 super.prepare(); 85 prepareChessboard(); 86 prepareBuffer(); 87 preparePipeline(); 88 prepareDescriptorSet(); 89 } 90 91 void prepareChessboard() 92 { 93 auto data = new ubyte[32*32]; 94 foreach (r; 0 .. 32) { 95 foreach (c; 0 .. 32) { 96 immutable oddR = (r/4)%2 != 0; 97 immutable oddC = (c/4)%2 != 0; 98 data[r*32 + c] = oddR == oddC ? 0xff : 0x00; 99 } 100 } 101 chessboard = createTextureImage( 102 cast(const(void)[])data, ImageInfo.d2(32, 32).withFormat(Format.r8_uNorm) 103 ); 104 chessboardView = chessboard.createView( 105 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 106 ); 107 import gfx.core.typecons : none; 108 sampler = device.createSampler(SamplerInfo( 109 Filter.nearest, Filter.nearest, Filter.nearest, 110 [WrapMode.repeat, WrapMode.repeat, WrapMode.repeat], 111 none!float, 0f, [0f, 0f] 112 )); 113 } 114 115 void prepareBuffer() 116 { 117 auto data = new ubyte[squareLen + triangleLen]; 118 data[0 .. squareLen] = cast(immutable(ubyte)[])square; 119 data[squareLen .. squareLen+triangleLen] = cast(immutable(ubyte[]))triangle; 120 vertBuf = createStaticBuffer(data, BufferUsage.vertex); 121 122 indBuf = createStaticBuffer(squareIndices, BufferUsage.index); 123 } 124 125 override void prepareRenderPass() 126 { 127 const attachments = [ 128 AttachmentDescription( 129 swapchain.format, 1, 130 AttachmentOps(LoadOp.clear, StoreOp.store), 131 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 132 trans(ImageLayout.undefined, ImageLayout.presentSrc), 133 No.mayAlias 134 ), 135 AttachmentDescription( 136 findStencilFormat(), 1, 137 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 138 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 139 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 140 No.mayAlias 141 ), 142 ]; 143 const subpasses = [ 144 // subpass 1: write stencil buffer from texture 145 // subpass 2: render with stencil masking 146 SubpassDescription( 147 [], [], some(AttachmentRef(1, ImageLayout.depthStencilAttachmentOptimal)), [] 148 ), 149 SubpassDescription( 150 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ], 151 some(AttachmentRef(1, ImageLayout.depthStencilAttachmentOptimal)), [] 152 ) 153 ]; 154 const dependencies = [ 155 SubpassDependency( 156 trans!uint(0, 1), 157 trans(PipelineStage.lateFragmentTests, PipelineStage.earlyFragmentTests), 158 trans(Access.depthStencilAttachmentWrite, Access.depthStencilAttachmentRead) 159 ) 160 ]; 161 162 renderPass = device.createRenderPass(attachments, subpasses, dependencies); 163 } 164 165 class StencilFrameData : FrameData 166 { 167 PrimaryCommandBuffer cmdBuf; 168 Rc!Image stencil; 169 Rc!Framebuffer framebuffer; 170 171 this(ImageBase swcColor, CommandBuffer tempBuf) 172 { 173 super(swcColor); 174 cmdBuf = cmdPool.allocatePrimary(1)[0]; 175 stencil = this.outer.createStencilImage(size[0], size[1]); 176 177 auto colorView = swcColor.createView( 178 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 179 ).rc; 180 auto stencilView = stencil.createView( 181 ImageType.d2, ImageSubresourceRange(ImageAspect.stencil), Swizzle.identity 182 ).rc; 183 184 this.framebuffer = this.outer.device.createFramebuffer(this.outer.renderPass, [ 185 colorView.obj, stencilView.obj 186 ], size[0], size[1], 1); 187 } 188 189 override void dispose() 190 { 191 framebuffer.unload(); 192 stencil.unload(); 193 cmdPool.free([ cast(CommandBuffer)cmdBuf ]); 194 super.dispose(); 195 } 196 } 197 198 override FrameData makeFrameData(ImageBase swcColor, CommandBuffer tempBuf) 199 { 200 return new StencilFrameData(swcColor, tempBuf); 201 } 202 203 void preparePipeline() 204 { 205 const stencilSpv = [ 206 import("stencil.vert.spv"), import("stencil.frag.spv") 207 ]; 208 auto swVs = device.createShaderModule( 209 cast(immutable(uint)[])stencilSpv[0], "main" 210 ).rc; 211 auto swFs = device.createShaderModule( 212 cast(immutable(uint)[])stencilSpv[1], "main" 213 ).rc; 214 stencilWriteDSL = device.createDescriptorSetLayout([ 215 PipelineLayoutBinding(0, DescriptorType.combinedImageSampler, 1, ShaderStage.fragment), 216 ]); 217 stencilWriteLayout = device.createPipelineLayout([ stencilWriteDSL.obj ], []); 218 219 PipelineInfo swInfo; 220 swInfo.shaders.vertex = swVs; 221 swInfo.shaders.fragment = swFs; 222 swInfo.inputBindings = [ 223 VertexInputBinding(0, VertexP2T2.sizeof, No.instanced) 224 ]; 225 swInfo.inputAttribs = [ 226 VertexInputAttrib(0, 0, Format.rg32_sFloat, VertexP2T2.position.offsetof), 227 VertexInputAttrib(1, 0, Format.rg32_sFloat, VertexP2T2.texCoord.offsetof), 228 ]; 229 swInfo.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 230 swInfo.rasterizer = Rasterizer( 231 PolygonMode.fill, Cull.none, FrontFace.ccw, No.depthClamp, 232 none!DepthBias, 1f 233 ); 234 swInfo.viewports = [ 235 ViewportConfig( 236 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]), 237 Rect(0, 0, surfaceSize[0], surfaceSize[1]) 238 ) 239 ]; 240 const sos1 = StencilOpState( 241 StencilOp.replace, StencilOp.replace, StencilOp.replace, CompareOp.always, 242 0x01, 0x01, 0x01 243 ); 244 swInfo.stencilInfo = StencilInfo( 245 Yes.enabled, sos1, sos1 246 ); 247 swInfo.layout = stencilWriteLayout; 248 swInfo.renderPass = renderPass; 249 swInfo.subpassIndex = 0; 250 251 252 const solidSpv = [ 253 import("solid.vert.spv"), import("solid.frag.spv") 254 ]; 255 auto solVs = device.createShaderModule( 256 cast(immutable(uint)[])solidSpv[0], "main" 257 ).rc; 258 auto solFs = device.createShaderModule( 259 cast(immutable(uint)[])solidSpv[1], "main" 260 ).rc; 261 auto solPL = device.createPipelineLayout([], []).rc; 262 263 PipelineInfo solInfo; 264 solInfo.shaders.vertex = solVs; 265 solInfo.shaders.fragment = solFs; 266 solInfo.inputBindings = [ 267 VertexInputBinding(0, VertexP2C3.sizeof, No.instanced) 268 ]; 269 solInfo.inputAttribs = [ 270 VertexInputAttrib(0, 0, Format.rg32_sFloat, VertexP2C3.position.offsetof), 271 VertexInputAttrib(1, 0, Format.rgb32_sFloat, VertexP2C3.color.offsetof), 272 ]; 273 solInfo.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 274 solInfo.rasterizer = Rasterizer( 275 PolygonMode.fill, Cull.none, FrontFace.ccw, No.depthClamp, 276 none!DepthBias, 1f 277 ); 278 solInfo.viewports = [ 279 ViewportConfig( 280 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]), 281 Rect(0, 0, surfaceSize[0], surfaceSize[1]) 282 ) 283 ]; 284 solInfo.blendInfo = ColorBlendInfo( 285 none!LogicOp, [ ColorBlendAttachment.solid() ], [ 0f, 0f, 0f, 0f ] 286 ); 287 const sos2 = StencilOpState( 288 StencilOp.keep, StencilOp.keep, StencilOp.keep, CompareOp.equal, 289 0x01, 0x01, 0x01 290 ); 291 solInfo.stencilInfo = StencilInfo( 292 Yes.enabled, sos2, sos2 293 ); 294 solInfo.layout = solPL; 295 solInfo.renderPass = renderPass; 296 solInfo.subpassIndex = 1; 297 298 auto pls = device.createPipelines( [ swInfo, solInfo ] ); 299 stencilWritePipeline = pls[0]; 300 solidPipeline = pls[1]; 301 } 302 303 void prepareDescriptorSet() { 304 const poolSizes = [ 305 DescriptorPoolSize(DescriptorType.combinedImageSampler, 1), 306 ]; 307 descPool = device.createDescriptorPool(1, poolSizes); 308 descriptorSet = descPool.allocate([ stencilWriteDSL.obj ])[0]; 309 310 auto writes = [ 311 WriteDescriptorSet(descriptorSet, 0, 0, DescriptorWrite.make( 312 DescriptorType.combinedImageSampler, 313 chessboardView.descriptorWithSampler(ImageLayout.undefined, sampler), 314 )), 315 ]; 316 device.updateDescriptorSets(writes, []); 317 } 318 319 320 override Submission[] recordCmds(FrameData frameData) 321 { 322 auto sfd = cast(StencilFrameData)frameData; 323 324 const cv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f); 325 const dsv = ClearDepthStencilValues(0f, 0); 326 327 auto buf = sfd.cmdBuf; 328 329 buf.begin(CommandBufferUsage.oneTimeSubmit); 330 331 buf.beginRenderPass( 332 renderPass, sfd.framebuffer, 333 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 334 [ ClearValues(cv), ClearValues(dsv) ] 335 ); 336 337 buf.bindPipeline(stencilWritePipeline); 338 buf.bindIndexBuffer(indBuf, 0, IndexType.u16); 339 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, 0) ]); 340 buf.bindDescriptorSets( 341 PipelineBindPoint.graphics, stencilWriteLayout, 0, 342 [ descriptorSet ], [] 343 ); 344 buf.drawIndexed(cast(uint)squareIndices.length, 1, 0, 0, 0); 345 346 buf.nextSubpass(); 347 348 buf.bindPipeline(solidPipeline); 349 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, squareLen) ]); 350 buf.draw(3, 1, 0, 0); 351 352 buf.endRenderPass(); 353 354 buf.end(); 355 356 return simpleSubmission([ buf ]); 357 } 358 359 } 360 361 int main(string[] args) 362 { 363 try { 364 auto example = new StencilExample(args); 365 example.prepare(); 366 scope(exit) example.dispose(); 367 368 example.window.onKeyOn = (KeyEvent ev) 369 { 370 if (ev.sym == KeySym.escape) { 371 example.window.closeFlag = true; 372 } 373 }; 374 375 while (!example.window.closeFlag) { 376 example.display.pollAndDispatch(); 377 example.render(); 378 example.frameTick(); 379 } 380 381 return 0; 382 } 383 catch(Exception ex) { 384 stderr.writeln("error occured: ", ex.msg); 385 return 1; 386 } 387 }