diff --git a/Makefile b/Makefile index f9bd3c9..4176d50 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ OBJS = \ src/tga/tga.o \ src/vulkan_helper.o \ src/collada/scene/vulkan.o \ + src/collada/scene/reload.o \ src/collada/scene.o \ src/collada/node_state.o \ src/collada/animate.o diff --git a/include/collada/scene.h b/include/collada/scene.h index 8ce355a..b665718 100644 --- a/include/collada/scene.h +++ b/include/collada/scene.h @@ -5,6 +5,7 @@ #include "collada/node_state.h" #include "collada/scene/vulkan.h" +#include "collada/scene/reload.h" namespace collada::scene { struct state { @@ -12,6 +13,7 @@ namespace collada::scene { node_state::state node_state; collada::scene::vulkan vulkan; + collada::scene::reload reload; void load_scene(types::descriptor const * const descriptor); void draw(); diff --git a/include/collada/scene/reload.h b/include/collada/scene/reload.h new file mode 100644 index 0000000..420b675 --- /dev/null +++ b/include/collada/scene/reload.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "collada/types.h" +#include "collada/scene/vulkan.h" + +namespace collada::scene { + struct reload_stat { + char * filenameTGA; + struct timespec mtime; + }; + + struct reload { + reload_stat * imageStats; + + void load_images(types::descriptor const * const descriptor); + void stat_images(collada::types::descriptor const * const descriptor, + collada::scene::vulkan & vulkan); + void destroy_images(types::descriptor const * const descriptor); + }; +} diff --git a/include/collada/scene/vulkan.h b/include/collada/scene/vulkan.h index 72d8aaa..1518261 100644 --- a/include/collada/scene/vulkan.h +++ b/include/collada/scene/vulkan.h @@ -148,6 +148,7 @@ namespace collada::scene { VkImageView shadowDepthImageView); void change_frame(VkCommandBuffer commandBuffer, uint32_t frameIndex); + void destroy_image(int i); void destroy_all(collada::types::descriptor const * const descriptor); ////////////////////////////////////////////////////////////////////// @@ -169,6 +170,8 @@ namespace collada::scene { void create_descriptor_sets(collada::types::descriptor const * const descriptor); void write_descriptor_sets(collada::types::descriptor const * const descriptor); void load_material_constants(collada::types::descriptor const * const descriptor); + void load_image_inner(VkCommandBuffer commandBuffer, VkFence fence, int i, char const * filename); + void load_image(int i, char const * filename); void load_images(collada::types::descriptor const * const descriptor); ////////////////////////////////////////////////////////////////////// diff --git a/include/file.h b/include/file.h index e9fda03..c77a78e 100644 --- a/include/file.h +++ b/include/file.h @@ -1,5 +1,7 @@ #pragma once namespace file { - void const * open(const char * r_filename, uint32_t * out_size); + void const * open(char const * filename, uint32_t * out_size); + + void * openRelative(char const * filename, uint32_t * out_size); } diff --git a/src/collada/scene.cpp b/src/collada/scene.cpp index f311bf2..91ca800 100644 --- a/src/collada/scene.cpp +++ b/src/collada/scene.cpp @@ -21,6 +21,7 @@ namespace collada::scene { vulkan.load_images(descriptor); vulkan.write_descriptor_sets(descriptor); vulkan.create_pipelines(descriptor); + reload.load_images(descriptor); node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count); } @@ -71,11 +72,14 @@ namespace collada::scene { animate::animate_node(node_state.node_instances[i], t); node_state.update_node_world_transform(node_state.node_instances[i]); } + + reload.stat_images(descriptor, vulkan); } void state::unload_scene() { node_state.deallocate_node_instances(descriptor->nodes_count); + reload.destroy_images(descriptor); } void state::mouse_motion(int eyeIndex, int targetIndex, float xrel, float yrel, int mode) diff --git a/src/collada/scene/reload.cpp b/src/collada/scene/reload.cpp new file mode 100644 index 0000000..13158ac --- /dev/null +++ b/src/collada/scene/reload.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include "new.h" +#include "collada/scene/reload.h" + +namespace collada::scene { + void reload::load_images(types::descriptor const * const descriptor) + { + imageStats = NewM(descriptor->images_count); + + for (int i = 0; i < descriptor->images_count; i++) { + char const * filename = descriptor->images[i]->uri; + size_t length = strlen(filename); + + imageStats[i].filenameTGA = strndup(filename, length); + imageStats[i].filenameTGA[length - 3] = 't'; + imageStats[i].filenameTGA[length - 2] = 'g'; + imageStats[i].filenameTGA[length - 1] = 'a'; + + imageStats[i].mtime.tv_sec = 0; + imageStats[i].mtime.tv_nsec = 0; + } + } + + void reload::stat_images(collada::types::descriptor const * const descriptor, + collada::scene::vulkan & vulkan) + { + bool reload = false; + for (int i = 0; i < descriptor->images_count; i++) { + off_t size = 0; + while (true) { + struct stat statbuf; + int ret = stat(imageStats[i].filenameTGA, &statbuf); + if (ret != 0) + break; + + if (statbuf.st_mtim.tv_sec != imageStats[i].mtime.tv_sec || statbuf.st_mtim.tv_nsec != imageStats[i].mtime.tv_nsec) { + if (statbuf.st_size != size) { + size = statbuf.st_size; + usleep(500); + continue; + } + + fprintf(stderr, "reload %s\n", imageStats[i].filenameTGA); + reload = true; + vulkan.destroy_image(i); + vulkan.load_image(i, imageStats[i].filenameTGA); + imageStats[i].mtime.tv_sec = statbuf.st_mtim.tv_sec; + imageStats[i].mtime.tv_nsec = statbuf.st_mtim.tv_nsec; + } + break; + } + } + if (reload) { + vulkan.write_descriptor_sets(descriptor); + } + } + + void reload::destroy_images(types::descriptor const * const descriptor) + { + for (int i = 0; i < descriptor->images_count; i++) { + free(imageStats[i].filenameTGA); + } + free(imageStats); + } +} diff --git a/src/collada/scene/vulkan.cpp b/src/collada/scene/vulkan.cpp index 7f14243..87166ba 100644 --- a/src/collada/scene/vulkan.cpp +++ b/src/collada/scene/vulkan.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "volk/volk.h" #include "vulkan/vk_enum_string_helper.h" @@ -79,7 +80,7 @@ inline static void vulkan_vertex_input_states(collada::types::descriptor const * VkVertexInputBindingDescription * vertexBindingDescriptions) { for (int i = 0; i < descriptor->inputs_list_count; i++) { - collada::types::inputs const & inputs = descriptor->inputs_list[i]; + collada::types::inputs const & inputs = descriptor->inputs_list[1]; VkVertexInputAttributeDescription * vertexAttributeDescriptions = NewM(inputs.elements_count + collada::inputs::skin_inputs.elements_count); uint32_t stride = vulkan_load_layout(inputs, 0, // binding @@ -651,6 +652,66 @@ namespace collada::scene { // material textures ////////////////////////////////////////////////////////////////////// + void vulkan::load_image_inner(VkCommandBuffer commandBuffer, VkFence fence, int i, char const * filename) + { + size_t length = strlen(filename); + if (dds::isDDSExtension(filename, length)) { + createImageFromFilenameDDS(device, + queue, + commandBuffer, + fence, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + filename, + &images[i].image, + &images[i].memory, + &images[i].imageView); + } else if (tga::isTGAExtension(filename, length)) { + createImageFromFilenameTGA(device, + queue, + commandBuffer, + fence, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + filename, + &images[i].image, + &images[i].memory, + &images[i].imageView); + } else { + fprintf(stderr, "filename: %s\n", filename); + ASSERT(false, "invalid image filename extension"); + } + } + + void vulkan::load_image(int i, char const * filename) + { + VkCommandBuffer commandBuffer{}; + VkCommandBufferAllocateInfo commandBufferAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = commandPool, + .commandBufferCount = 1 + }; + VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer)); + + VkFenceCreateInfo fenceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO + }; + VkFence fence{}; + VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); + + // load + + load_image_inner(commandBuffer, fence, i, filename); + + // cleanup + + vkDestroyFence(device, fence, nullptr); + vkFreeCommandBuffers(device, + commandPool, + 1, + &commandBuffer); + } + void vulkan::load_images(collada::types::descriptor const * const descriptor) { VkCommandBuffer commandBuffer{}; @@ -672,33 +733,7 @@ namespace collada::scene { for (int i = 0; i < descriptor->images_count; i++) { char const * filename = descriptor->images[i]->uri; - size_t length = strlen(filename); - if (dds::isDDSExtension(filename, length)) { - createImageFromFilenameDDS(device, - queue, - commandBuffer, - fence, - physicalDeviceProperties.limits.nonCoherentAtomSize, - physicalDeviceMemoryProperties, - filename, - &images[i].image, - &images[i].memory, - &images[i].imageView); - } else if (tga::isTGAExtension(filename, length)) { - createImageFromFilenameTGA(device, - queue, - commandBuffer, - fence, - physicalDeviceProperties.limits.nonCoherentAtomSize, - physicalDeviceMemoryProperties, - filename, - &images[i].image, - &images[i].memory, - &images[i].imageView); - } else { - fprintf(stderr, "filename: %s\n", filename); - ASSERT(false, "invalid image filename extension"); - } + load_image_inner(commandBuffer, fence, i, filename); } // cleanup @@ -1179,12 +1214,17 @@ namespace collada::scene { 0, nullptr); } + void vulkan::destroy_image(int i) + { + vkDestroyImage(device, images[i].image, nullptr); + vkDestroyImageView(device, images[i].imageView, nullptr); + vkFreeMemory(device, images[i].memory, nullptr); + } + void vulkan::destroy_all(collada::types::descriptor const * const descriptor) { for (int i = 0; i < descriptor->images_count; i++) { - vkDestroyImage(device, images[i].image, nullptr); - vkDestroyImageView(device, images[i].imageView, nullptr); - vkFreeMemory(device, images[i].memory, nullptr); + destroy_image(i); } free(images); diff --git a/src/file.cpp b/src/file.cpp index 1cc6afc..3a67801 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "pack.h" #include "file.h" @@ -23,9 +24,9 @@ extern "C" { namespace file { - void const * open(const char * r_filename, uint32_t * out_size) + void const * open(const char * filename, uint32_t * out_size) { - fprintf(stderr, "(pack) filename: %s\n", r_filename); + fprintf(stderr, "(pack) filename: %s\n", filename); pack::header const * header = (pack::header const *)&files_pack_start[0]; if (header->magic != pack::magic_value) { @@ -35,13 +36,53 @@ namespace file { ptrdiff_t data = (ptrdiff_t)&files_pack_start[header->header_size]; for (unsigned int i = 0; i < header->entry_count; i++) { - if (strcmp(header->entry[i].filename, r_filename) == 0) { + if (strcmp(header->entry[i].filename, filename) == 0) { *out_size = header->entry[i].size; return (void const *)(data + header->entry[i].offset); } } - fprintf(stderr, "filename not found in pack file %s\n", r_filename); + fprintf(stderr, "filename not found in pack file %s\n", filename); exit(EXIT_FAILURE); } + + void * openRelative(char const * filename, uint32_t * out_size) + { + FILE * f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno)); + return NULL; + } + + int fseek_end_ret = fseek(f, 0, SEEK_END); + if (fseek_end_ret < 0) { + fprintf(stderr, "fseek(%s, SEEK_END): %s\n", filename, strerror(errno)); + return NULL; + } + + size_t size = ftell(f); + if (size < 0) { + fprintf(stderr, "ftell(%s): %s\n", filename, strerror(errno)); + return NULL; + } + + int fseek_set_ret = fseek(f, 0, SEEK_SET); + if (fseek_set_ret < 0) { + fprintf(stderr, "lseek(%s, SEEK_SET): %s\n", filename, strerror(errno)); + return NULL; + } + rewind(f); + + void * buf = malloc(size); + + size_t read_size = fread(buf, 1, size, f); + if (read_size != size) { + fprintf(stderr, "fread(%s): %s\n", filename, strerror(errno)); + return NULL; + } + + *out_size = size; + + return buf; + } } diff --git a/src/tga/tga.cpp b/src/tga/tga.cpp index ac58e7b..0b81b33 100644 --- a/src/tga/tga.cpp +++ b/src/tga/tga.cpp @@ -19,6 +19,7 @@ namespace tga { size_t imageOffset = (sizeof (header)) + tga->idLength; *outData = (void *)(((size_t)data) + imageOffset); *outSize = tga->image.width * tga->image.width * bytesPerPixel; + assert(*outSize <= (size - imageOffset)); return tga; } diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index 2ae9f45..7669599 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -379,7 +379,8 @@ void createImageFromFilenameTGA(VkDevice device, VkImageView * outImageView) { uint32_t imageSize; - void const * imageStart = file::open(filename, &imageSize); + //void const * imageStart = file::open(filename, &imageSize); + void * imageStart = file::openRelative(filename, &imageSize); void * imageData; uint32_t imageDataSize; tga::header const * tga = tga::validate(imageStart, imageSize, &imageData, &imageDataSize); @@ -415,5 +416,5 @@ void createImageFromFilenameTGA(VkDevice device, levelCount, &levelOffset); - // imageData is not malloc'ed, it is a pointer to file:: data, which is also not malloc'ed + free(imageStart); }