Mercurial > games > semicongine
comparison semiconginev2/old/vulkan/image.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/old/vulkan/image.nim@a3eb305bcac2 |
children |
comparison
equal
deleted
inserted
replaced
1217:f819a874058f | 1218:56781cc0fc7c |
---|---|
1 import std/strformat | |
2 import std/tables | |
3 import std/logging | |
4 | |
5 import ../core | |
6 import ./device | |
7 import ./physicaldevice | |
8 import ./buffer | |
9 import ./memory | |
10 import ./commandbuffer | |
11 | |
12 type | |
13 PixelDepth = 1 .. 4 | |
14 VulkanImage* = object | |
15 device*: Device | |
16 vk*: VkImage | |
17 width*: uint32 # pixel | |
18 height*: uint32 # pixel | |
19 depth*: PixelDepth | |
20 format*: VkFormat | |
21 usage*: seq[VkImageUsageFlagBits] | |
22 case memoryAllocated*: bool | |
23 of false: discard | |
24 of true: | |
25 memory*: DeviceMemory | |
26 VulkanSampler* = object | |
27 device*: Device | |
28 vk*: VkSampler | |
29 ImageView* = object | |
30 vk*: VkImageView | |
31 image*: VulkanImage | |
32 VulkanTexture* = object | |
33 image*: VulkanImage | |
34 imageView*: ImageView | |
35 sampler*: VulkanSampler | |
36 | |
37 const DEPTH_FORMAT_MAP = { | |
38 PixelDepth(1): [VK_FORMAT_R8_SRGB, VK_FORMAT_R8_UNORM], | |
39 PixelDepth(2): [VK_FORMAT_R8G8_SRGB, VK_FORMAT_R8G8_UNORM], | |
40 PixelDepth(3): [VK_FORMAT_R8G8B8_SRGB, VK_FORMAT_R8G8B8_UNORM], | |
41 PixelDepth(4): [VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_R8G8B8A8_UNORM], | |
42 }.toTable | |
43 | |
44 const LINEAR_FORMATS = [ | |
45 VK_FORMAT_R8_UNORM, | |
46 VK_FORMAT_R8G8_UNORM, | |
47 VK_FORMAT_R8G8B8_UNORM, | |
48 VK_FORMAT_R8G8B8A8_UNORM, | |
49 ] | |
50 | |
51 | |
52 proc requirements(image: VulkanImage): MemoryRequirements = | |
53 assert image.vk.Valid | |
54 assert image.device.vk.Valid | |
55 var req: VkMemoryRequirements | |
56 image.device.vk.vkGetImageMemoryRequirements(image.vk, addr req) | |
57 result.size = req.size | |
58 result.alignment = req.alignment | |
59 let memorytypes = image.device.physicaldevice.vk.GetMemoryProperties().types | |
60 for i in 0 ..< sizeof(req.memoryTypeBits) * 8: | |
61 if ((req.memoryTypeBits shr i) and 1) == 1: | |
62 result.memoryTypes.add memorytypes[i] | |
63 | |
64 proc allocateMemory(image: var VulkanImage, requireMappable: bool, preferVRAM: bool, preferAutoFlush: bool) = | |
65 assert image.device.vk.Valid | |
66 assert image.memoryAllocated == false | |
67 | |
68 let requirements = image.requirements() | |
69 let memoryType = requirements.memoryTypes.SelectBestMemoryType( | |
70 requireMappable = requireMappable, | |
71 preferVRAM = preferVRAM, | |
72 preferAutoFlush = preferAutoFlush | |
73 ) | |
74 | |
75 debug "Allocating memory for image: ", image.width, "x", image.height, "x", image.depth, ", ", requirements.size, " bytes of type ", memoryType | |
76 image = VulkanImage( | |
77 device: image.device, | |
78 vk: image.vk, | |
79 width: image.width, | |
80 height: image.height, | |
81 depth: image.depth, | |
82 format: image.format, | |
83 usage: image.usage, | |
84 memoryAllocated: true, | |
85 memory: image.device.Allocate(requirements.size, memoryType), | |
86 ) | |
87 checkVkResult image.device.vk.vkBindImageMemory(image.vk, image.memory.vk, VkDeviceSize(0)) | |
88 | |
89 proc transitionImageLayout(image: VulkanImage, queue: Queue, oldLayout, newLayout: VkImageLayout) = | |
90 var | |
91 barrier = VkImageMemoryBarrier( | |
92 sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
93 oldLayout: oldLayout, | |
94 newLayout: newLayout, | |
95 srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, | |
96 dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED, | |
97 image: image.vk, | |
98 subresourceRange: VkImageSubresourceRange( | |
99 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], | |
100 baseMipLevel: 0, | |
101 levelCount: 1, | |
102 baseArrayLayer: 0, | |
103 layerCount: 1, | |
104 ), | |
105 ) | |
106 srcStage: VkPipelineStageFlagBits | |
107 dstStage: VkPipelineStageFlagBits | |
108 if oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: | |
109 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | |
110 barrier.srcAccessMask = VkAccessFlags(0) | |
111 dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT | |
112 barrier.dstAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits | |
113 elif oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: | |
114 srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT | |
115 barrier.srcAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits | |
116 dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | |
117 barrier.dstAccessMask = [VK_ACCESS_SHADER_READ_BIT].toBits | |
118 else: | |
119 raise newException(Exception, "Unsupported layout transition!") | |
120 | |
121 WithSingleUseCommandBuffer(image.device, queue, commandBuffer): | |
122 commandBuffer.PipelineBarrier([srcStage], [dstStage], imageBarriers = [barrier]) | |
123 | |
124 proc Copy*(src: Buffer, dst: VulkanImage, queue: Queue) = | |
125 assert src.device.vk.Valid | |
126 assert dst.device.vk.Valid | |
127 assert src.device == dst.device | |
128 assert VK_BUFFER_USAGE_TRANSFER_SRC_BIT in src.usage | |
129 assert VK_IMAGE_USAGE_TRANSFER_DST_BIT in dst.usage | |
130 | |
131 var region = VkBufferImageCopy( | |
132 bufferOffset: 0, | |
133 bufferRowLength: 0, | |
134 bufferImageHeight: 0, | |
135 imageSubresource: VkImageSubresourceLayers( | |
136 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], | |
137 mipLevel: 0, | |
138 baseArrayLayer: 0, | |
139 layerCount: 1, | |
140 ), | |
141 imageOffset: VkOffset3D(x: 0, y: 0, z: 0), | |
142 imageExtent: VkExtent3D(width: dst.width, height: dst.height, depth: 1) | |
143 ) | |
144 WithSingleUseCommandBuffer(src.device, queue, commandBuffer): | |
145 commandBuffer.vkCmdCopyBufferToImage( | |
146 src.vk, | |
147 dst.vk, | |
148 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
149 1, | |
150 addr region | |
151 ) | |
152 | |
153 proc CreateImage*(device: Device, width, height, depth: uint32, format: VkFormat, samples: VkSampleCountFlagBits, usage: openArray[VkImageUsageFlagBits]): VulkanImage = | |
154 assert device.vk.Valid | |
155 assert width > 0 | |
156 assert height > 0 | |
157 | |
158 result.device = device | |
159 result.usage = @usage | |
160 | |
161 | |
162 result.width = width | |
163 result.height = height | |
164 result.depth = depth | |
165 result.format = format | |
166 | |
167 var imageInfo = VkImageCreateInfo( | |
168 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
169 imageType: VK_IMAGE_TYPE_2D, | |
170 extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1), | |
171 mipLevels: 1, | |
172 arrayLayers: 1, | |
173 format: result.format, | |
174 tiling: VK_IMAGE_TILING_OPTIMAL, | |
175 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, | |
176 usage: toBits result.usage, | |
177 sharingMode: VK_SHARING_MODE_EXCLUSIVE, | |
178 samples: samples, | |
179 ) | |
180 checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk) | |
181 result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false) | |
182 | |
183 # currently only usable for texture access from shader | |
184 proc createTextureImage[T](device: Device, queue: Queue, width, height: uint32, depth: PixelDepth, image: Image[T]): VulkanImage = | |
185 assert device.vk.Valid | |
186 assert width > 0 | |
187 assert height > 0 | |
188 assert depth != 2 | |
189 | |
190 result.device = device | |
191 result.usage = @[VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_USAGE_SAMPLED_BIT] | |
192 | |
193 let size: uint64 = width * height * uint32(depth) | |
194 let usageBits = toBits result.usage | |
195 var formatList = DEPTH_FORMAT_MAP[depth] | |
196 var selectedFormat: VkFormat | |
197 var formatProperties = VkImageFormatProperties2(sType: VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2) | |
198 | |
199 for format in formatList: | |
200 var formatInfo = VkPhysicalDeviceImageFormatInfo2( | |
201 sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, | |
202 format: format, | |
203 thetype: VK_IMAGE_TYPE_2D, | |
204 tiling: VK_IMAGE_TILING_OPTIMAL, | |
205 usage: usageBits, | |
206 ) | |
207 let formatCheck = device.physicalDevice.vk.vkGetPhysicalDeviceImageFormatProperties2( | |
208 addr formatInfo, | |
209 addr formatProperties, | |
210 ) | |
211 if formatCheck == VK_SUCCESS: # found suitable format | |
212 selectedFormat = format | |
213 break | |
214 elif formatCheck == VK_ERROR_FORMAT_NOT_SUPPORTED: # nope, try to find other format | |
215 continue | |
216 else: # raise error | |
217 checkVkResult formatCheck | |
218 | |
219 # assumption: images comes in sRGB color space | |
220 # convert to linear space if there is not support for sRGB | |
221 var data = addr image.imagedata[0] | |
222 if selectedFormat in LINEAR_FORMATS: | |
223 let linearImage = image.AsLinear() | |
224 data = addr linearImage.imagedata[0] | |
225 | |
226 assert size <= uint64(formatProperties.imageFormatProperties.maxResourceSize) | |
227 assert width <= uint64(formatProperties.imageFormatProperties.maxExtent.width) | |
228 assert height <= uint64(formatProperties.imageFormatProperties.maxExtent.height) | |
229 | |
230 result.width = width | |
231 result.height = height | |
232 result.depth = depth | |
233 result.format = selectedFormat | |
234 | |
235 var imageInfo = VkImageCreateInfo( | |
236 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
237 imageType: VK_IMAGE_TYPE_2D, | |
238 extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1), | |
239 mipLevels: min(1'u32, formatProperties.imageFormatProperties.maxMipLevels), | |
240 arrayLayers: min(1'u32, formatProperties.imageFormatProperties.maxArrayLayers), | |
241 format: result.format, | |
242 tiling: VK_IMAGE_TILING_OPTIMAL, | |
243 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, | |
244 usage: usageBits, | |
245 sharingMode: VK_SHARING_MODE_EXCLUSIVE, | |
246 samples: VK_SAMPLE_COUNT_1_BIT, | |
247 ) | |
248 checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk) | |
249 result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false) | |
250 result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) | |
251 | |
252 var stagingBuffer = device.CreateBuffer(size = size, usage = [VK_BUFFER_USAGE_TRANSFER_SRC_BIT], requireMappable = true, preferVRAM = false, preferAutoFlush = true) | |
253 stagingBuffer.SetData(queue, src = data, size = size) | |
254 stagingBuffer.Copy(result, queue) | |
255 result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) | |
256 stagingBuffer.Destroy() | |
257 | |
258 proc Destroy*(image: var VulkanImage) = | |
259 assert image.device.vk.Valid | |
260 assert image.vk.Valid | |
261 image.device.vk.vkDestroyImage(image.vk, nil) | |
262 if image.memoryAllocated: | |
263 assert image.memory.vk.Valid | |
264 image.memory.Free() | |
265 image = VulkanImage( | |
266 device: image.device, | |
267 vk: image.vk, | |
268 width: image.width, | |
269 height: image.height, | |
270 depth: image.depth, | |
271 format: image.format, | |
272 usage: image.usage, | |
273 memoryAllocated: false, | |
274 ) | |
275 image.vk.Reset | |
276 | |
277 proc CreateSampler*(device: Device, sampler: Sampler): VulkanSampler = | |
278 assert device.vk.Valid | |
279 var samplerInfo = VkSamplerCreateInfo( | |
280 sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | |
281 magFilter: sampler.magnification, | |
282 minFilter: sampler.minification, | |
283 addressModeU: sampler.wrapModeS, | |
284 addressModeV: sampler.wrapModeT, | |
285 addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
286 anisotropyEnable: device.enabledFeatures.samplerAnisotropy, | |
287 maxAnisotropy: device.physicalDevice.properties.limits.maxSamplerAnisotropy, | |
288 borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK, | |
289 unnormalizedCoordinates: VK_FALSE, | |
290 compareEnable: VK_FALSE, | |
291 compareOp: VK_COMPARE_OP_ALWAYS, | |
292 mipmapMode: VK_SAMPLER_MIPMAP_MODE_LINEAR, | |
293 mipLodBias: 0, | |
294 minLod: 0, | |
295 maxLod: 0, | |
296 ) | |
297 result.device = device | |
298 checkVkResult device.vk.vkCreateSampler(addr samplerInfo, nil, addr result.vk) | |
299 | |
300 proc Destroy*(sampler: var VulkanSampler) = | |
301 assert sampler.device.vk.Valid | |
302 assert sampler.vk.Valid | |
303 sampler.device.vk.vkDestroySampler(sampler.vk, nil) | |
304 sampler.vk.Reset | |
305 | |
306 proc CreateImageView*( | |
307 image: VulkanImage, | |
308 imageviewtype = VK_IMAGE_VIEW_TYPE_2D, | |
309 baseMipLevel = 0'u32, | |
310 levelCount = 1'u32, | |
311 baseArrayLayer = 0'u32, | |
312 layerCount = 1'u32 | |
313 ): ImageView = | |
314 assert image.device.vk.Valid | |
315 assert image.vk.Valid | |
316 | |
317 var createInfo = VkImageViewCreateInfo( | |
318 sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
319 image: image.vk, | |
320 viewType: imageviewtype, | |
321 format: image.format, | |
322 components: VkComponentMapping( | |
323 r: VK_COMPONENT_SWIZZLE_IDENTITY, | |
324 g: VK_COMPONENT_SWIZZLE_IDENTITY, | |
325 b: VK_COMPONENT_SWIZZLE_IDENTITY, | |
326 a: VK_COMPONENT_SWIZZLE_IDENTITY, | |
327 ), | |
328 subresourceRange: VkImageSubresourceRange( | |
329 aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), | |
330 baseMipLevel: baseMipLevel, | |
331 levelCount: levelCount, | |
332 baseArrayLayer: baseArrayLayer, | |
333 layerCount: layerCount, | |
334 ), | |
335 ) | |
336 result.image = image | |
337 checkVkResult image.device.vk.vkCreateImageView(addr(createInfo), nil, addr(result.vk)) | |
338 | |
339 proc Destroy*(imageview: var ImageView) = | |
340 assert imageview.image.device.vk.Valid | |
341 assert imageview.vk.Valid | |
342 imageview.image.device.vk.vkDestroyImageView(imageview.vk, nil) | |
343 imageview.vk.Reset() | |
344 | |
345 func `$`*(texture: VulkanTexture): string = | |
346 &"VulkanTexture({texture.image.width}x{texture.image.height})" | |
347 | |
348 | |
349 proc UploadTexture*(device: Device, queue: Queue, texture: Texture): VulkanTexture = | |
350 assert device.vk.Valid | |
351 if texture.isGrayscale: | |
352 result.image = createTextureImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, image = texture.grayImage) | |
353 else: | |
354 result.image = createTextureImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, image = texture.colorImage) | |
355 result.imageView = result.image.CreateImageView() | |
356 result.sampler = result.image.device.CreateSampler(texture.sampler) | |
357 | |
358 proc Destroy*(texture: var VulkanTexture) = | |
359 texture.image.Destroy() | |
360 texture.imageView.Destroy() | |
361 texture.sampler.Destroy() |