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 }