diff options
Diffstat (limited to 'video.cpp')
-rw-r--r-- | video.cpp | 302 |
1 files changed, 255 insertions, 47 deletions
@@ -192,6 +192,65 @@ struct Hash_Map { } }; +struct Vram_Allocator { + static constexpr int size_alignment = (1024 * 1024 * 32); + struct Page; + struct Chunk; + struct Allocation { + VkDeviceMemory mem; + Page* page; + Chunk* chunk; + bool valid() const { return chunk != 0; } + VkDeviceSize offset() const { return chunk->get_offset(); } + void* map(VkDeviceSize off) { + assert(page->mapping != 0); + return (char*)page->mapping + offset() + off; + } + }; + struct Chunk { + VkDeviceSize offset; + VkDeviceSize pad; + VkDeviceSize size; + Chunk* next; + bool free; + + VkDeviceSize get_offset() { + return offset + pad; + } + }; + struct Page { + VkDeviceMemory memory; + VkDeviceSize size; + int type; + Chunk* chunks; + Page* next; + void* mapping; + /* need something better for host-writable, + * non-coherent mappings */ + + void init(Device_Vk* dev, VkDeviceSize size, int type); + void defrag(Device_Vk* dev); + Allocation imp_alloc(Device_Vk* dev, VkDeviceSize size); + Allocation alloc( + Device_Vk* dev, + VkDeviceSize size, + VkDeviceSize align + ); + }; + Page* pages; + Device_Vk* dev; + + void init(Device_Vk* d); + void destroy(); + Allocation alloc( + int type, + VkDeviceSize size, + VkDeviceSize align + ); + void free(Allocation& alloc); +}; + + static VkFormat get_vk_format(Texture_Format fmt) { switch (fmt) { case texture_format_r8i: return VK_FORMAT_R8_UNORM; @@ -510,7 +569,7 @@ struct Context_Vk : public Context { struct Texture_Vk : public Texture, public Late_Terminated { VkImage image; VkImageView view; - VkDeviceMemory memory; + Vram_Allocator::Allocation memory; Resource_State state; void destroy(Device_Vk*) override; @@ -518,8 +577,8 @@ struct Texture_Vk : public Texture, public Late_Terminated { struct Buffer_Vk : public Buffer, public Late_Terminated { VkBuffer buf; - VkDeviceMemory memory; VkDeviceSize size; + Vram_Allocator::Allocation memory; int flags; void init(Device_Vk* dev, int flags, VkDeviceSize size); @@ -861,6 +920,7 @@ struct Device_Vk : public Device { Context_Vk* current_ctx; Shader_Loader shader_loader; Texture_Loader texture_loader; + Vram_Allocator vrama; #ifdef DEBUG VkDebugUtilsMessengerEXT msg; #endif @@ -1266,6 +1326,7 @@ void Device_Vk::init_internal() { #endif surf = app_create_vk_surface(app, inst); create_dev(&swap_cap); + vrama.init(this); gladLoaderLoadVulkan(inst, phys_dev, dev); vkGetDeviceQueue(dev, (uint32_t)queue_index, 0, &queue); terminators = 0; @@ -1329,6 +1390,7 @@ void Device_Vk::deinit_internal() { for (i = 0; i < image_count; i++) { terminators[i].execute(this); } + vrama.destroy(); vkDestroyDevice(dev, &ac); #ifdef DEBUG destroy_dmesg( @@ -3078,30 +3140,18 @@ void Buffer_Vk::init( { VkMemoryPropertyFlags props = get_memory_flags(flags); int mt = dev->find_memory_type(req.memoryTypeBits, props); - VkMemoryAllocateInfo ai{}; - if (mt < 0) { - print("Failed to find a satisfying memory type index.\n"); - pbreak(mt); - } - ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - ai.allocationSize = req.size; - ai.memoryTypeIndex = mt; - r = vkAllocateMemory(dev->dev, &ai, &dev->ac, &memory); - if (r == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - print_err("Out of video memory.\n"); - pbreak(r); - } - if (r != VK_SUCCESS) { - print_err("VRAM allocation failed.\n"); - pbreak(r); + memory = dev->vrama.alloc(mt, req.size, req.alignment); + if (!memory.valid()) { + print_err("Failed to allocate memory for buffer.\n"); + pbreak(900); } } - vkBindBufferMemory(dev->dev, buf, memory, 0); + vkBindBufferMemory(dev->dev, buf, memory.mem, memory.offset()); } void Buffer_Vk::destroy(Device_Vk* dev) { vkDestroyBuffer(dev->dev, buf, &dev->ac); - vkFreeMemory(dev->dev, memory, &dev->ac); + dev->vrama.free(memory); dev->buffers.remove(id); } @@ -3124,24 +3174,16 @@ void* Device::map_buffer( size_t offset, size_t size ) { - Device_Vk* dev = (Device_Vk*)this; Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); - void* ptr; - vkMapMemory( - dev->dev, - buf.memory, - (VkDeviceSize)offset, - (VkDeviceSize)size, - 0, - &ptr - ); - return ptr; + (void)size; + return buf.memory.map(offset); } void Device::unmap_buffer(Buffer_Id id) { - Device_Vk* dev = (Device_Vk*)this; + (void)id; +/* Device_Vk* dev = (Device_Vk*)this; Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); - vkUnmapMemory(dev->dev, buf.memory); + vkUnmapMemory(dev->dev, buf.memory.mem);*/ } Buffer& Device::get_buffer(Buffer_Id id) { @@ -3188,25 +3230,22 @@ Texture_Id Device::create_texture( VkMemoryPropertyFlags props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; int mt = dev->find_memory_type(req.memoryTypeBits, props); - VkMemoryAllocateInfo ai{}; if (mt < 0) { print("Failed to find a satisfying memory type index.\n"); pbreak(mt); } - ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - ai.allocationSize = req.size; - ai.memoryTypeIndex = mt; - r = vkAllocateMemory(dev->dev, &ai, &dev->ac, &tex.memory); - if (r == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - print_err("Out of video memory.\n"); - pbreak(r); - } - if (r != VK_SUCCESS) { - print_err("VRAM allocation failed.\n"); - pbreak(r); + tex.memory = dev->vrama.alloc(mt, req.size, req.alignment); + if (!tex.memory.valid()) { + print_err("Failed to allocate memory for texture.\n"); + pbreak(900); } } - vkBindImageMemory(dev->dev, tex.image, tex.memory, 0); + vkBindImageMemory( + dev->dev, + tex.image, + tex.memory.mem, + tex.memory.offset() + ); tex.w = w; tex.h = h; tex.fmt = fmt; @@ -3443,7 +3482,7 @@ void Texture_Loader::unload(Asset* a) { void Texture_Vk::destroy(Device_Vk* dev) { if (!alias) { vkDestroyImage(dev->dev, image, &dev->ac); - vkFreeMemory(dev->dev, memory, &dev->ac); + dev->vrama.free(memory); } vkDestroyImageView(dev->dev, view, &dev->ac); dev->textures.remove(id); @@ -3519,3 +3558,172 @@ void Sampler_Vk::init(Device_Vk* dev, const Sampler_State& s) { void Sampler_Vk::destroy(Device_Vk* dev) { vkDestroySampler(dev->dev, sampler, &dev->ac); } + +void Vram_Allocator::Page::init( + Device_Vk* dev, + VkDeviceSize s, + int t +) { + VkMemoryAllocateInfo ai{}; + Chunk* chunk; + VkResult r; + const auto& props = dev->mem_props.memoryTypes[t]; + ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + ai.allocationSize = s; + ai.memoryTypeIndex = t; + size = s; + type = t; + next = 0; + r = vkAllocateMemory(dev->dev, &ai, &dev->ac, &memory); + if (r == VK_ERROR_OUT_OF_DEVICE_MEMORY) { + print_err("Out of VRAM.\n"); + pbreak(r); + } + if (r != VK_SUCCESS) { + print_err("vkAllocateMemory failed.\n"); + pbreak(r); + } + if ( + props.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT || + props.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + ) { + vkMapMemory( + dev->dev, + memory, + 0, + size, + 0, + &mapping + ); + } else + mapping = 0; + chunk = (Chunk*)heap_alloc(dev->heap, sizeof *chunk); + chunk->offset = 0; + chunk->pad = 0; + chunk->size = s; + chunk->next = 0; + chunk->free = true; + chunks = chunk; +} + +Vram_Allocator::Allocation Vram_Allocator::Page::imp_alloc( + Device_Vk* dev, + VkDeviceSize asize +) { + Chunk* chunk; + for (chunk = chunks; chunk; chunk = chunk->next) { + if (chunk->free) { + if (chunk->size == asize) { + chunk->free = false; + return { memory, 0, chunk }; + } else if (chunk->size > asize) { + Chunk* nc = (Chunk*)heap_alloc(dev->heap, sizeof *nc); + nc->offset = chunk->offset + asize; + nc->pad = 0; + nc->size = chunk->size - asize; + nc->next = chunk->next; + nc->free = true; + chunk->next = nc; + chunk->size = asize; + chunk->pad = 0; + chunk->free = false; + return { memory, 0, chunk }; + } + } + } + return { 0, 0, 0 }; +} + +void Vram_Allocator::Page::defrag(Device_Vk* dev) { + Chunk* chunk; + for (chunk = chunks; chunk;) { + if (chunk->free) { + Chunk* end = chunk->next; + VkDeviceSize csize = chunk->size; + for (; end && end->free;) { + Chunk* next = end->next; + csize += end->size; + heap_free(dev->heap, end); + end = next; + } + chunk->next = end; + chunk->size = csize; + if (end) { + chunk = end->next; + } else chunk = 0; + } else + chunk = chunk->next; + } +} + +Vram_Allocator::Allocation Vram_Allocator::Page::alloc( + Device_Vk* dev, + VkDeviceSize asize, + VkDeviceSize align +) { + VkDeviceSize as = asize + align; + VkDeviceSize al; + Allocation a = imp_alloc(dev, as); + if (!a.chunk) { + defrag(dev); + a = imp_alloc(dev, as); + } + if (!a.chunk) return a; + al = align_address((uintptr_t)a.chunk->offset, (size_t)align); + a.chunk->pad = al - a.chunk->offset; + return a; +} + +void Vram_Allocator::init(Device_Vk* d) { + pages = 0; + dev = d; +} + +void Vram_Allocator::destroy() { + Page* page = pages; + for (; page; page = page->next) { + Chunk* chunk = page->chunks; + if (page->mapping) + vkUnmapMemory(dev->dev, page->memory); + vkFreeMemory(dev->dev, page->memory, &dev->ac); + for (; chunk; chunk = chunk->next) + heap_free(dev->heap, chunk); + heap_free(dev->heap, page); + } +} + +Vram_Allocator::Allocation Vram_Allocator::alloc( + int type, + VkDeviceSize size, + VkDeviceSize align +) { + Page* page = pages; + for (; page; page = page->next) { + if (page->type == type) { + auto a = page->alloc(dev, size, align); + if (a.chunk) { + a.page = page; + return a; + } + } + } + page = (Page*)heap_alloc(dev->heap, sizeof *page); + page->init( + dev, + (VkDeviceSize)align_address( + (uintptr_t)size, + (size_t)size_alignment + ), + type + ); + page->next = pages; + pages = page; + auto a = page->alloc(dev, size, align); + if (a.chunk) + a.page = page; + return a; +} + +void Vram_Allocator::free(Allocation& alloc) { + alloc.chunk->free = true; +} |