1 module sbylib.graphics.glyph.glyphtexture;
2 
3 import std;
4 import erupted;
5 import sbylib.wrapper.freetype;
6 import sbylib.wrapper.vulkan;
7 import sbylib.graphics.wrapper.device;
8 import sbylib.graphics.util.own;
9 import sbylib.graphics.wrapper;
10 
11 class GlyphTexture : Texture {
12 
13     private {
14         @own {
15             VImage image;
16             ImageView _imageView;
17             Sampler _sampler;
18             VCommandBuffer commandBuffer;
19             VFence fence;
20         }
21         uint _width, _height;
22     }
23 
24     mixin ImplReleaseOwn;
25 
26     this(uint _width, uint _height) {
27         this._width = _width;
28         this._height = _height;
29 
30         this.image = createImage(width, height);
31         this._imageView = createImageView(image);
32 
33         this._sampler = createSampler();
34 
35         this.commandBuffer = VCommandBuffer.allocate(VCommandBuffer.Type.Graphics);
36 
37         this.fence = VFence.create("glyph texture fence");
38     }
39 
40     private VImage createImage(int width, int height) {
41         Image.CreateInfo imageInfo = {
42             imageType: ImageType.Type2D,
43             extent: {
44                 width: width,
45                 height: height,
46                 depth: 1
47             },
48             mipLevels: 1,
49             arrayLayers: 1,
50             format: VK_FORMAT_R8_UNORM,
51             tiling: ImageTiling.Optimal,
52             initialLayout: ImageLayout.ShaderReadOnlyOptimal,
53             usage: ImageUsage.TransferSrc | ImageUsage.TransferDst | ImageUsage.Sampled,
54             sharingMode: SharingMode.Exclusive,
55             samples: SampleCount.Count1
56         };
57         return new VImage(new Image(VDevice(), imageInfo), MemoryProperties.MemoryType.Flags.DeviceLocal);
58     }
59 
60     private ImageView createImageView(VImage image) {
61         ImageView.CreateInfo imageViewInfo = {
62             image: image.image,
63             viewType: ImageViewType.Type2D,
64             format: VK_FORMAT_R8_UNORM,
65             subresourceRange: {
66                 aspectMask: ImageAspect.Color,
67                 baseMipLevel: 0,
68                 levelCount: 1,
69                 baseArrayLayer: 0,
70                 layerCount: 1
71             }
72         };
73         return new ImageView(VDevice(), imageViewInfo);
74     }
75 
76     private Sampler createSampler() {
77         Sampler.CreateInfo samplerInfo = {
78             magFilter: SamplerFilter.Linear,
79             minFilter: SamplerFilter.Linear,
80             addressModeU: SamplerAddressMode.Repeat,
81             addressModeV: SamplerAddressMode.Repeat,
82             addressModeW: SamplerAddressMode.Repeat,
83             anisotropyEnable: false,
84             maxAnisotropy: 1,
85             borderColor: BorderColor.IntOpaqueBlack,
86             unnormalizedCoordinates: false,
87             compareEnable: false,
88             compareOp: CompareOp.Always,
89             mipmapMode: SamplerMipmapMode.Linear,
90             mipLodBias: 0.0f,
91             minLod: 0.0f,
92             maxLod: 0.0f
93         };
94         return new Sampler(VDevice(), samplerInfo);
95     }
96 
97     package void write(ubyte[] data, VkOffset3D offset, VkExtent3D extent) {
98         auto stagingBuffer = new VBuffer!ubyte(data, BufferUsage.TransferSrc);
99         scope (exit)
100             stagingBuffer.destroy();
101 
102         with (commandBuffer(CommandBuffer.BeginInfo.Flags.OneTimeSubmit)) {
103             /*
104                Transit image layout from `Undefined` to `TransferDstOptimal`.
105                After transit, accessability of image becomes `TransferWrite`.
106                Target subrange is color part, mimap level 0..1 (only 0), and array layer 0..1 (only 0).
107                This transition is done after waiting at top of pipe of before command (does not wait at all).
108                This transition is finished by transfer stage of next command.
109              */
110             VkImageMemoryBarrier barrier = {
111                 dstAccessMask: AccessFlags.TransferWrite,
112                 oldLayout: ImageLayout.Undefined,
113                 newLayout: ImageLayout.TransferDstOptimal,
114                 image: image.image.image,
115                 subresourceRange: {
116                     aspectMask: ImageAspect.Color,
117                     baseMipLevel: 0,
118                     levelCount: 1,
119                     baseArrayLayer: 0,
120                     layerCount: 1
121                 }
122             };
123             cmdPipelineBarrier(PipelineStage.TopOfPipe, PipelineStage.Transfer, 0, [], [], [barrier]);
124 
125             /*
126                Copy data from staging buffer to image.
127                `bufferOffset` is as it looks.
128                `bufferRowLength` and `bufferImageHeight` can control the arrangement of the data. and if both are 0, that means data is tightly packed.
129                `imageSubresource` is the same as above.
130                `imageOffset` and `imageExtent` are also as they look.
131                At this operation, image layout must be `TransferDstOptimal`
132              */
133             VkBufferImageCopy bufferImageCopy = {
134                 bufferOffset: 0,
135                 bufferRowLength: 0,
136                 bufferImageHeight: 0,
137                 imageSubresource: {
138                     aspectMask: ImageAspect.Color,
139                     mipLevel: 0,
140                     baseArrayLayer: 0,
141                     layerCount: 1,
142                 },
143                 imageOffset: offset,
144                 imageExtent: extent,
145             };
146             cmdCopyBufferToImage(stagingBuffer.buffer, image.image, ImageLayout.TransferDstOptimal, [bufferImageCopy]);
147 
148             VkImageMemoryBarrier barrier2 = {
149                 oldLayout: ImageLayout.TransferDstOptimal,
150                 newLayout: ImageLayout.ShaderReadOnlyOptimal,
151                 image: image.image.image,
152                 subresourceRange: {
153                     aspectMask: ImageAspect.Color,
154                     baseMipLevel: 0,
155                     levelCount: 1,
156                     baseArrayLayer: 0,
157                     layerCount: 1
158                 }
159             };
160             cmdPipelineBarrier(PipelineStage.Transfer, PipelineStage.FragmentShader, 0, [], [], [barrier2]);
161         }
162 
163         VQueue(VQueue.Type.Graphics).submit(commandBuffer);
164     }
165 
166     uint width() const {
167         return _width;
168     }
169 
170     uint height() const {
171         return _height;
172     }
173 
174     override ImageView imageView() {
175         return _imageView;
176     }
177 
178     override Sampler sampler() {
179         return _sampler;
180     }
181 
182     void resize(uint newWidth, uint newHeight) {
183         auto oldImage = this.image;
184         scope (exit)
185             oldImage.destroy();
186 
187         auto oldImageView = this.imageView;
188         scope (exit)
189             oldImageView.destroy();
190 
191         auto newImage = createImage(newWidth, newHeight);
192         auto newImageView = createImageView(newImage);
193 
194         copy(oldImage, newImage, width, height);
195 
196         this._width = newWidth;
197         this._height = newHeight;
198         this.image = newImage;
199         this._imageView = newImageView;
200     }
201 
202     private void copy(VImage oldImage, VImage newImage, uint oldWidth, uint oldHeight) {
203         with (commandBuffer(CommandBuffer.BeginInfo.Flags.OneTimeSubmit)) {
204             VkImageMemoryBarrier[2] barrier = [{
205                 dstAccessMask: AccessFlags.TransferRead,
206                 oldLayout: ImageLayout.Undefined,
207                 newLayout: ImageLayout.TransferSrcOptimal,
208                 image: oldImage.image.image,
209                 subresourceRange: {
210                     aspectMask: ImageAspect.Color,
211                     baseMipLevel: 0,
212                     levelCount: 1,
213                     baseArrayLayer: 0,
214                     layerCount: 1
215                 }
216             }, {
217                 dstAccessMask: AccessFlags.TransferWrite,
218                 oldLayout: ImageLayout.Undefined,
219                 newLayout: ImageLayout.TransferDstOptimal,
220                 image: newImage.image.image,
221                 subresourceRange: {
222                     aspectMask: ImageAspect.Color,
223                     baseMipLevel: 0,
224                     levelCount: 1,
225                     baseArrayLayer: 0,
226                     layerCount: 1
227                 }
228             }];
229             cmdPipelineBarrier(PipelineStage.TopOfPipe, PipelineStage.Transfer, 0, [], [], barrier);
230 
231             VkImageBlit region = {
232                 srcSubresource: {
233                     aspectMask: ImageAspect.Color,
234                     mipLevel: 0,
235                     baseArrayLayer: 0,
236                     layerCount: 1
237                 },
238                 srcOffsets: [{
239                     x: 0,
240                     y: 0,
241                     z: 0
242                 }, {
243                     x: oldWidth,
244                     y: oldHeight,
245                     z: 1
246                 }],
247                 dstSubresource: {
248                     aspectMask: ImageAspect.Color,
249                     mipLevel: 0,
250                     baseArrayLayer: 0,
251                     layerCount: 1
252                 },
253                 dstOffsets: [{
254                     x: 0,
255                     y: 0,
256                     z: 0
257                 }, {
258                     x: oldWidth,
259                     y: oldHeight,
260                     z: 1
261                 }],
262             };
263             cmdBlitImage(
264                     oldImage.image, ImageLayout.TransferSrcOptimal,
265                     newImage.image, ImageLayout.TransferDstOptimal,
266                     [region], SamplerFilter.Nearest);
267         }
268 
269         VQueue(VQueue.Type.Graphics).submitWithFence(commandBuffer, fence);
270         fence.wait();
271         fence.reset();
272     }
273 }