1 module triangle;
2 
3 import example;
4 
5 import gfx.core.rc;
6 import gfx.core.typecons;
7 import gfx.graal.buffer;
8 import gfx.graal.cmd;
9 import gfx.graal.device;
10 import gfx.graal.format;
11 import gfx.graal.image;
12 import gfx.graal.memory;
13 import gfx.graal.pipeline;
14 import gfx.graal.presentation;
15 import gfx.graal.queue;
16 import gfx.graal.renderpass;
17 import gfx.graal.types;
18 
19 import std.exception;
20 import std.stdio;
21 import std.typecons;
22 
23 class TriangleExample : Example
24 {
25     Rc!RenderPass renderPass;
26     Framebuffer[] framebuffers;
27     Rc!Pipeline pipeline;
28     PerImage[] perImages;
29     Rc!Buffer vertBuf;
30 
31     struct PerImage {
32         bool undefinedLayout=true;
33     }
34 
35     struct Vertex {
36         float[4] position;
37         float[4] color;
38     }
39 
40     this(string[] args) {
41         super("Triangle", args);
42     }
43 
44     override void dispose() {
45         if (device) {
46             device.waitIdle();
47         }
48         vertBuf.unload();
49         pipeline.unload();
50         renderPass.unload();
51         releaseArr(framebuffers);
52         super.dispose();
53     }
54 
55     override void prepare() {
56         super.prepare();
57         prepareBuffer();
58         prepareRenderPass();
59         preparePipeline();
60     }
61 
62     void prepareBuffer() {
63         const vertexData = [
64             Vertex([-0.7f,  0.7f, 0f, 1f], [ 0f, 1f, 0f, 1f ]),
65             Vertex([ 0.7f,  0.7f, 0f, 1f], [ 1f, 0f, 0f, 1f ]),
66             Vertex([   0f, -0.7f, 0f, 1f], [ 0f, 0f, 1f, 1f ]),
67         ];
68 
69         vertBuf = createStaticBuffer(vertexData, BufferUsage.vertex);
70     }
71 
72     void prepareRenderPass() {
73         const attachments = [
74             AttachmentDescription(swapchain.format, 1,
75                 AttachmentOps(LoadOp.clear, StoreOp.store),
76                 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare),
77                 trans(ImageLayout.presentSrc, ImageLayout.presentSrc),
78                 No.mayAlias
79             )
80         ];
81         const subpasses = [
82             SubpassDescription(
83                 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ],
84                 none!AttachmentRef, []
85             )
86         ];
87 
88         renderPass = device.createRenderPass(attachments, subpasses, []);
89 
90         framebuffers = new Framebuffer[scImages.length];
91         foreach (i; 0 .. scImages.length) {
92             framebuffers[i] = device.createFramebuffer(renderPass, [
93                 scImages[i].createView(
94                     ImageType.d2,
95                     ImageSubresourceRange(ImageAspect.color),
96                     Swizzle.identity
97                 )
98             ], surfaceSize[0], surfaceSize[1], 1);
99         }
100         retainArr(framebuffers);
101     }
102 
103     void preparePipeline()
104     {
105         auto vtxShader = device.createShaderModule(
106             cast(immutable(uint)[])import("shader.vert.spv"), "main"
107         ).rc;
108         auto fragShader = device.createShaderModule(
109             cast(immutable(uint)[])import("shader.frag.spv"), "main"
110         ).rc;
111 
112         PipelineInfo info;
113         info.shaders.vertex = vtxShader;
114         info.shaders.fragment = fragShader;
115         info.inputBindings = [
116             VertexInputBinding(0, Vertex.sizeof, No.instanced)
117         ];
118         info.inputAttribs = [
119             VertexInputAttrib(0, 0, Format.rgba32_sFloat, 0),
120             VertexInputAttrib(1, 0, Format.rgba32_sFloat, Vertex.color.offsetof),
121         ];
122         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
123         info.rasterizer = Rasterizer(
124             PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp,
125             none!DepthBias, 1f
126         );
127         info.viewports = [
128             ViewportConfig(
129                 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]),
130                 Rect(0, 0, surfaceSize[0], surfaceSize[1])
131             )
132         ];
133         info.blendInfo = ColorBlendInfo(
134             none!LogicOp, [
135                 ColorBlendAttachment(No.enabled,
136                     BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
137                     BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
138                     ColorMask.all
139                 )
140             ],
141             [ 0f, 0f, 0f, 0f ]
142         );
143         info.layout = device.createPipelineLayout([], []);
144         info.renderPass = renderPass;
145         info.subpassIndex = 0;
146 
147         auto pls = device.createPipelines( [info] );
148         pipeline = pls[0];
149     }
150 
151 
152     override void recordCmds(size_t cmdBufInd, size_t imgInd) {
153         import gfx.core.typecons : trans;
154 
155         if (!perImages.length) {
156             perImages = new PerImage[scImages.length];
157         }
158 
159         const cv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f);
160         auto subrange = ImageSubresourceRange(ImageAspect.color, 0, 1, 0, 1);
161 
162         auto buf = cmdBufs[cmdBufInd];
163 
164         //buf.reset();
165         buf.begin(No.persistent);
166 
167         if (perImages[imgInd].undefinedLayout) {
168             buf.pipelineBarrier(
169                 trans(PipelineStage.colorAttachmentOutput, PipelineStage.colorAttachmentOutput), [],
170                 [ ImageMemoryBarrier(
171                     trans(Access.none, Access.colorAttachmentWrite),
172                     trans(ImageLayout.undefined, ImageLayout.presentSrc),
173                     trans(graphicsQueueIndex, graphicsQueueIndex),
174                     scImages[imgInd], subrange
175                 ) ]
176             );
177             perImages[imgInd].undefinedLayout = false;
178         }
179 
180         buf.beginRenderPass(
181             renderPass, framebuffers[imgInd],
182             Rect(0, 0, surfaceSize[0], surfaceSize[1]), [ ClearValues(cv) ]
183         );
184 
185         buf.bindPipeline(pipeline);
186         buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, 0) ]);
187         buf.draw(3, 1, 0, 0);
188 
189         buf.endRenderPass();
190 
191         buf.end();
192     }
193 
194 }
195 
196 int main(string[] args) {
197 
198     try {
199         auto example = new TriangleExample(args);
200         example.prepare();
201         scope(exit) example.dispose();
202 
203         example.window.onMouseOn = (uint, uint) {
204             example.window.closeFlag = true;
205         };
206 
207         import std.datetime.stopwatch : StopWatch;
208 
209         uint frameCount;
210         ulong lastUs;
211         StopWatch sw;
212         sw.start();
213 
214         enum reportFreq = 100;
215 
216         while (!example.window.closeFlag) {
217             example.display.pollAndDispatch();
218             example.render();
219             ++ frameCount;
220             if ((frameCount % reportFreq) == 0) {
221                 const us = sw.peek().total!"usecs";
222                 writeln("FPS: ", 1000_000.0 * reportFreq / (us - lastUs));
223                 lastUs = us;
224             }
225         }
226 
227         return 0;
228     }
229     catch(Exception ex) {
230         stderr.writeln("error occured: ", ex.msg);
231         return 1;
232     }
233 }