scene/reload: implement run-time texture reload
This commit is contained in:
parent
352958f031
commit
9f636548db
1
Makefile
1
Makefile
@ -45,6 +45,7 @@ OBJS = \
|
|||||||
src/tga/tga.o \
|
src/tga/tga.o \
|
||||||
src/vulkan_helper.o \
|
src/vulkan_helper.o \
|
||||||
src/collada/scene/vulkan.o \
|
src/collada/scene/vulkan.o \
|
||||||
|
src/collada/scene/reload.o \
|
||||||
src/collada/scene.o \
|
src/collada/scene.o \
|
||||||
src/collada/node_state.o \
|
src/collada/node_state.o \
|
||||||
src/collada/animate.o
|
src/collada/animate.o
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "collada/node_state.h"
|
#include "collada/node_state.h"
|
||||||
|
|
||||||
#include "collada/scene/vulkan.h"
|
#include "collada/scene/vulkan.h"
|
||||||
|
#include "collada/scene/reload.h"
|
||||||
|
|
||||||
namespace collada::scene {
|
namespace collada::scene {
|
||||||
struct state {
|
struct state {
|
||||||
@ -12,6 +13,7 @@ namespace collada::scene {
|
|||||||
node_state::state node_state;
|
node_state::state node_state;
|
||||||
|
|
||||||
collada::scene::vulkan vulkan;
|
collada::scene::vulkan vulkan;
|
||||||
|
collada::scene::reload reload;
|
||||||
|
|
||||||
void load_scene(types::descriptor const * const descriptor);
|
void load_scene(types::descriptor const * const descriptor);
|
||||||
void draw();
|
void draw();
|
||||||
|
|||||||
22
include/collada/scene/reload.h
Normal file
22
include/collada/scene/reload.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -148,6 +148,7 @@ namespace collada::scene {
|
|||||||
VkImageView shadowDepthImageView);
|
VkImageView shadowDepthImageView);
|
||||||
|
|
||||||
void change_frame(VkCommandBuffer commandBuffer, uint32_t frameIndex);
|
void change_frame(VkCommandBuffer commandBuffer, uint32_t frameIndex);
|
||||||
|
void destroy_image(int i);
|
||||||
void destroy_all(collada::types::descriptor const * const descriptor);
|
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 create_descriptor_sets(collada::types::descriptor const * const descriptor);
|
||||||
void write_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_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);
|
void load_images(collada::types::descriptor const * const descriptor);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace file {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace collada::scene {
|
|||||||
vulkan.load_images(descriptor);
|
vulkan.load_images(descriptor);
|
||||||
vulkan.write_descriptor_sets(descriptor);
|
vulkan.write_descriptor_sets(descriptor);
|
||||||
vulkan.create_pipelines(descriptor);
|
vulkan.create_pipelines(descriptor);
|
||||||
|
reload.load_images(descriptor);
|
||||||
|
|
||||||
node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count);
|
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);
|
animate::animate_node(node_state.node_instances[i], t);
|
||||||
node_state.update_node_world_transform(node_state.node_instances[i]);
|
node_state.update_node_world_transform(node_state.node_instances[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reload.stat_images(descriptor, vulkan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void state::unload_scene()
|
void state::unload_scene()
|
||||||
{
|
{
|
||||||
node_state.deallocate_node_instances(descriptor->nodes_count);
|
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)
|
void state::mouse_motion(int eyeIndex, int targetIndex, float xrel, float yrel, int mode)
|
||||||
|
|||||||
69
src/collada/scene/reload.cpp
Normal file
69
src/collada/scene/reload.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "new.h"
|
||||||
|
#include "collada/scene/reload.h"
|
||||||
|
|
||||||
|
namespace collada::scene {
|
||||||
|
void reload::load_images(types::descriptor const * const descriptor)
|
||||||
|
{
|
||||||
|
imageStats = NewM<reload_stat>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "volk/volk.h"
|
#include "volk/volk.h"
|
||||||
#include "vulkan/vk_enum_string_helper.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)
|
VkVertexInputBindingDescription * vertexBindingDescriptions)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < descriptor->inputs_list_count; i++) {
|
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<VkVertexInputAttributeDescription>(inputs.elements_count + collada::inputs::skin_inputs.elements_count);
|
VkVertexInputAttributeDescription * vertexAttributeDescriptions = NewM<VkVertexInputAttributeDescription>(inputs.elements_count + collada::inputs::skin_inputs.elements_count);
|
||||||
uint32_t stride = vulkan_load_layout(inputs,
|
uint32_t stride = vulkan_load_layout(inputs,
|
||||||
0, // binding
|
0, // binding
|
||||||
@ -651,6 +652,66 @@ namespace collada::scene {
|
|||||||
// material textures
|
// 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)
|
void vulkan::load_images(collada::types::descriptor const * const descriptor)
|
||||||
{
|
{
|
||||||
VkCommandBuffer commandBuffer{};
|
VkCommandBuffer commandBuffer{};
|
||||||
@ -672,33 +733,7 @@ namespace collada::scene {
|
|||||||
|
|
||||||
for (int i = 0; i < descriptor->images_count; i++) {
|
for (int i = 0; i < descriptor->images_count; i++) {
|
||||||
char const * filename = descriptor->images[i]->uri;
|
char const * filename = descriptor->images[i]->uri;
|
||||||
size_t length = strlen(filename);
|
load_image_inner(commandBuffer, fence, i, 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
@ -1179,12 +1214,17 @@ namespace collada::scene {
|
|||||||
0, nullptr);
|
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)
|
void vulkan::destroy_all(collada::types::descriptor const * const descriptor)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < descriptor->images_count; i++) {
|
for (int i = 0; i < descriptor->images_count; i++) {
|
||||||
vkDestroyImage(device, images[i].image, nullptr);
|
destroy_image(i);
|
||||||
vkDestroyImageView(device, images[i].imageView, nullptr);
|
|
||||||
vkFreeMemory(device, images[i].memory, nullptr);
|
|
||||||
}
|
}
|
||||||
free(images);
|
free(images);
|
||||||
|
|
||||||
|
|||||||
49
src/file.cpp
49
src/file.cpp
@ -3,6 +3,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include "pack.h"
|
#include "pack.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
@ -23,9 +24,9 @@ extern "C" {
|
|||||||
|
|
||||||
namespace file {
|
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];
|
pack::header const * header = (pack::header const *)&files_pack_start[0];
|
||||||
if (header->magic != pack::magic_value) {
|
if (header->magic != pack::magic_value) {
|
||||||
@ -35,13 +36,53 @@ namespace file {
|
|||||||
ptrdiff_t data = (ptrdiff_t)&files_pack_start[header->header_size];
|
ptrdiff_t data = (ptrdiff_t)&files_pack_start[header->header_size];
|
||||||
|
|
||||||
for (unsigned int i = 0; i < header->entry_count; i++) {
|
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;
|
*out_size = header->entry[i].size;
|
||||||
return (void const *)(data + header->entry[i].offset);
|
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);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ namespace tga {
|
|||||||
size_t imageOffset = (sizeof (header)) + tga->idLength;
|
size_t imageOffset = (sizeof (header)) + tga->idLength;
|
||||||
*outData = (void *)(((size_t)data) + imageOffset);
|
*outData = (void *)(((size_t)data) + imageOffset);
|
||||||
*outSize = tga->image.width * tga->image.width * bytesPerPixel;
|
*outSize = tga->image.width * tga->image.width * bytesPerPixel;
|
||||||
|
assert(*outSize <= (size - imageOffset));
|
||||||
|
|
||||||
return tga;
|
return tga;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -379,7 +379,8 @@ void createImageFromFilenameTGA(VkDevice device,
|
|||||||
VkImageView * outImageView)
|
VkImageView * outImageView)
|
||||||
{
|
{
|
||||||
uint32_t imageSize;
|
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;
|
void * imageData;
|
||||||
uint32_t imageDataSize;
|
uint32_t imageDataSize;
|
||||||
tga::header const * tga = tga::validate(imageStart, imageSize, &imageData, &imageDataSize);
|
tga::header const * tga = tga::validate(imageStart, imageSize, &imageData, &imageDataSize);
|
||||||
@ -415,5 +416,5 @@ void createImageFromFilenameTGA(VkDevice device,
|
|||||||
levelCount,
|
levelCount,
|
||||||
&levelOffset);
|
&levelOffset);
|
||||||
|
|
||||||
// imageData is not malloc'ed, it is a pointer to file:: data, which is also not malloc'ed
|
free(imageStart);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user