diff --git a/Makefile b/Makefile index e622c54..f9bd3c9 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ CFLAGS += -I./data CFLAGS += -I../SDL3-dist/include CFLAGS += -fpic -#FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address +FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address LDFLAGS += -lm ifeq ($(UNAME),Linux) @@ -42,6 +42,7 @@ OBJS = \ src/file.o \ src/pack.o \ src/dds/validate.o \ + src/tga/tga.o \ src/vulkan_helper.o \ src/collada/scene/vulkan.o \ src/collada/scene.o \ diff --git a/data/scenes/eidelwind/0_EidelWindTextureTest.dds b/data/scenes/eidelwind/0_EidelWindTextureTest.dds deleted file mode 100644 index 7113f8b..0000000 Binary files a/data/scenes/eidelwind/0_EidelWindTextureTest.dds and /dev/null differ diff --git a/include/dds/validate.h b/include/dds/validate.h index 31ea478..36ece60 100644 --- a/include/dds/validate.h +++ b/include/dds/validate.h @@ -10,4 +10,18 @@ struct DDS_FILE { namespace dds { DDS_FILE const * validate(void const * data, uint32_t size, uint32_t ** out_offsets, void ** out_data, uint32_t * out_size); + + static inline bool isDDSExtension(const char * filename, size_t length) + { + char a = filename[length - 4]; + char b = filename[length - 3]; + char c = filename[length - 2]; + char d = filename[length - 1]; + + return + (a == '.') && + (b == 'd' || b == 'D') && + (c == 'd' || c == 'D') && + (d == 's' || d == 'S'); + } } diff --git a/include/tga/tga.h b/include/tga/tga.h new file mode 100644 index 0000000..34bf2d5 --- /dev/null +++ b/include/tga/tga.h @@ -0,0 +1,43 @@ +#include + +#define PACKED __attribute__((packed)) + +namespace tga { + struct PACKED header { + uint8_t idLength; + uint8_t colorMapType; + uint8_t imageTypeCode; + struct PACKED { + uint16_t origin; + uint16_t length; + uint8_t depth; + } colorMap; + struct PACKED { + uint16_t xOrigin; + uint16_t yOrigin; + uint16_t width; + uint16_t height; + uint8_t bitsPerPixel; + } image; + uint8_t descriptor; + }; + static_assert((sizeof (header)) == 18); + + header const * validate(void const * data, uint32_t size, void ** outData, uint32_t * outSize); + + static inline bool isTGAExtension(const char * filename, size_t length) + { + char a = filename[length - 4]; + char b = filename[length - 3]; + char c = filename[length - 2]; + char d = filename[length - 1]; + + return + (a == '.') && + (b == 't' || b == 'T') && + (c == 'g' || c == 'G') && + (d == 'a' || d == 'A'); + } +} + +#undef PACKED diff --git a/include/vulkan_helper.h b/include/vulkan_helper.h index e3fac96..9b3be79 100644 --- a/include/vulkan_helper.h +++ b/include/vulkan_helper.h @@ -62,3 +62,14 @@ void createImageFromFilenameDDS(VkDevice device, VkImage * outImage, VkDeviceMemory * outMemory, VkImageView * outImageView); + +void createImageFromFilenameTGA(VkDevice device, + VkQueue queue, + VkCommandBuffer commandBuffer, + VkFence fence, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + char const * const filename, + VkImage * outImage, + VkDeviceMemory * outMemory, + VkImageView * outImageView); diff --git a/src/collada/scene/vulkan.cpp b/src/collada/scene/vulkan.cpp index 9338882..7f14243 100644 --- a/src/collada/scene/vulkan.cpp +++ b/src/collada/scene/vulkan.cpp @@ -13,6 +13,7 @@ #include "vulkan_helper.h" #include "dds/validate.h" #include "dds/vulkan.h" +#include "tga/tga.h" #include "check.h" #include "new.h" @@ -670,16 +671,34 @@ namespace collada::scene { images = NewM(descriptor->images_count); for (int i = 0; i < descriptor->images_count; i++) { - createImageFromFilenameDDS(device, - queue, - commandBuffer, - fence, - physicalDeviceProperties.limits.nonCoherentAtomSize, - physicalDeviceMemoryProperties, - descriptor->images[i]->uri, - &images[i].image, - &images[i].memory, - &images[i].imageView); + 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"); + } } // cleanup diff --git a/src/tga/tga.cpp b/src/tga/tga.cpp new file mode 100644 index 0000000..ac58e7b --- /dev/null +++ b/src/tga/tga.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include "tga/tga.h" + +namespace tga { + header const * validate(void const * data, uint32_t size, void ** outData, uint32_t * outSize) + { + header const * const tga = (tga::header const *)data; + + assert(tga->colorMapType == 0); + assert(tga->imageTypeCode == 2); + assert(tga->image.xOrigin == 0); + assert(tga->image.yOrigin == 0); + assert(tga->image.bitsPerPixel == 32); + + + uint32_t bytesPerPixel = tga->image.bitsPerPixel / 8; + size_t imageOffset = (sizeof (header)) + tga->idLength; + *outData = (void *)(((size_t)data) + imageOffset); + *outSize = tga->image.width * tga->image.width * bytesPerPixel; + + return tga; + } +} diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index fc1dba0..2ae9f45 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -13,6 +13,7 @@ #include "dds/validate.h" #include "dds/vulkan.h" +#include "tga/tga.h" #include "vulkan_helper.h" @@ -115,85 +116,25 @@ VkDeviceSize allocateFromMemoryRequirements2(VkDevice device, return memoryAllocateInfo.allocationSize; } -void createImageFromFilenameDDS(VkDevice device, - VkQueue queue, - VkCommandBuffer commandBuffer, - VkFence fence, - VkDeviceSize nonCoherentAtomSize, - VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, - char const * const filename, - VkImage * outImage, - VkDeviceMemory * outMemory, - VkImageView * outImageView) +// ddsFile->header.dwWidth +// ddsFile->header.dwHeight +// ddsFile->header.dwMipMapCount +// uint32_t * mipOffsets; + +void textureTransfer(VkDevice device, + VkQueue queue, + VkCommandBuffer commandBuffer, + VkFence fence, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + uint32_t imageDataSize, + void * imageData, + VkImage image, + uint32_t width, + uint32_t height, + uint32_t levelCount, + uint32_t * levelOffsets) { - uint32_t imageSize; - void const * imageStart = file::open(filename, &imageSize); - void * imageData; - uint32_t * mipOffsets; - uint32_t imageDataSize; - DDS_FILE const * ddsFile = dds::validate(imageStart, imageSize, &mipOffsets, &imageData, &imageDataSize); - - VkFormat format = dds::dxgi_to_vulkan(ddsFile->header10.dxgiFormat); - - // image - - VkImage image; - VkImageCreateInfo imageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = format, - .extent = { - .width = ddsFile->header.dwWidth, - .height = ddsFile->header.dwHeight, - .depth = 1 - }, - .mipLevels = ddsFile->header.dwMipMapCount, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED - }; - VK_CHECK(vkCreateImage(device, &imageCreateInfo, nullptr, &image)); - *outImage = image; - - // image view - - VkDeviceMemory imageMemory; - VkMemoryRequirements imageMemoryRequirements; - vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements); - VkMemoryPropertyFlags imageMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; - VkMemoryAllocateFlags imageMemoryAllocateFlags{ }; - VkDeviceSize stride; - allocateFromMemoryRequirements(device, - nonCoherentAtomSize, - physicalDeviceMemoryProperties, - imageMemoryRequirements, - imageMemoryPropertyFlags, - imageMemoryAllocateFlags, - 1, - &imageMemory, - &stride); - *outMemory = imageMemory; - VK_CHECK(vkBindImageMemory(device, image, imageMemory, 0)); - - VkImageView imageView; - VkImageViewCreateInfo textureViewCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .levelCount = ddsFile->header.dwMipMapCount, - .layerCount = 1 - } - }; - VK_CHECK(vkCreateImageView(device, &textureViewCreateInfo, nullptr, &imageView)); - *outImageView = imageView; - - // texture transfer: source buffer - VkBuffer sourceBuffer{}; VkBufferCreateInfo sourceBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -243,7 +184,7 @@ void createImageFromFilenameDDS(VkDevice device, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .levelCount = ddsFile->header.dwMipMapCount, + .levelCount = levelCount, .layerCount = 1 } }; @@ -253,24 +194,23 @@ void createImageFromFilenameDDS(VkDevice device, .pImageMemoryBarriers = &imageBarrier }; vkCmdPipelineBarrier2(commandBuffer, &imageDependencyInfo); - VkBufferImageCopy * copyRegions = NewM(ddsFile->header.dwMipMapCount); - for (uint32_t level = 0; level < ddsFile->header.dwMipMapCount; level++) { + VkBufferImageCopy * copyRegions = NewM(levelCount); + for (uint32_t level = 0; level < levelCount; level++) { copyRegions[level] = { - .bufferOffset = mipOffsets[level], + .bufferOffset = levelOffsets[level], .imageSubresource{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = level, .layerCount = 1 }, .imageExtent{ - .width = max(1u, ddsFile->header.dwWidth >> level), - .height = max(1u, ddsFile->header.dwHeight >> level), + .width = max(1u, width >> level), + .height = max(1u, height >> level), .depth = 1 }, }; } - vkCmdCopyBufferToImage(commandBuffer, sourceBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ddsFile->header.dwMipMapCount, copyRegions); - free(mipOffsets); + vkCmdCopyBufferToImage(commandBuffer, sourceBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, levelCount, copyRegions); free(copyRegions); VkImageMemoryBarrier2 readBarrier{ @@ -282,7 +222,7 @@ void createImageFromFilenameDDS(VkDevice device, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, .image = image, - .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = ddsFile->header.dwMipMapCount, .layerCount = 1 } + .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = levelCount, .layerCount = 1 } }; VkDependencyInfo readDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, @@ -305,3 +245,175 @@ void createImageFromFilenameDDS(VkDevice device, vkDestroyBuffer(device, sourceBuffer, nullptr); vkFreeMemory(device, sourceBufferMemory, nullptr); } + +void createImage(VkDevice device, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + VkFormat format, + uint32_t width, + uint32_t height, + uint32_t levelCount, + VkImage * outImage, + VkDeviceMemory * outMemory, + VkImageView * outImageView) +{ + // image + + VkImage image; + VkImageCreateInfo imageCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = { + .width = width, + .height = height, + .depth = 1 + }, + .mipLevels = levelCount, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED + }; + VK_CHECK(vkCreateImage(device, &imageCreateInfo, nullptr, &image)); + *outImage = image; + + // image view + + VkDeviceMemory imageMemory; + VkMemoryRequirements imageMemoryRequirements; + vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements); + VkMemoryPropertyFlags imageMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; + VkMemoryAllocateFlags imageMemoryAllocateFlags{ }; + VkDeviceSize stride; + allocateFromMemoryRequirements(device, + nonCoherentAtomSize, + physicalDeviceMemoryProperties, + imageMemoryRequirements, + imageMemoryPropertyFlags, + imageMemoryAllocateFlags, + 1, + &imageMemory, + &stride); + *outMemory = imageMemory; + VK_CHECK(vkBindImageMemory(device, image, imageMemory, 0)); + + VkImageView imageView; + VkImageViewCreateInfo textureViewCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = levelCount, + .layerCount = 1 + } + }; + VK_CHECK(vkCreateImageView(device, &textureViewCreateInfo, nullptr, &imageView)); + *outImageView = imageView; +} + +void createImageFromFilenameDDS(VkDevice device, + VkQueue queue, + VkCommandBuffer commandBuffer, + VkFence fence, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + char const * const filename, + VkImage * outImage, + VkDeviceMemory * outMemory, + VkImageView * outImageView) +{ + uint32_t imageSize; + void const * imageStart = file::open(filename, &imageSize); + void * imageData; + uint32_t * levelOffsets; + uint32_t imageDataSize; + DDS_FILE const * ddsFile = dds::validate(imageStart, imageSize, &levelOffsets, &imageData, &imageDataSize); + + VkFormat format = dds::dxgi_to_vulkan(ddsFile->header10.dxgiFormat); + uint32_t width = ddsFile->header.dwWidth; + uint32_t height = ddsFile->header.dwHeight; + uint32_t levelCount = ddsFile->header.dwMipMapCount; + + createImage(device, + nonCoherentAtomSize, + physicalDeviceMemoryProperties, + format, + width, + height, + levelCount, + outImage, + outMemory, + outImageView); + + textureTransfer(device, + queue, + commandBuffer, + fence, + nonCoherentAtomSize, + physicalDeviceMemoryProperties, + imageDataSize, + imageData, + *outImage, + width, + height, + levelCount, + levelOffsets); + + free(levelOffsets); + // imageData is not malloc'ed, it is a pointer to file:: data, which is also not malloc'ed +} + +void createImageFromFilenameTGA(VkDevice device, + VkQueue queue, + VkCommandBuffer commandBuffer, + VkFence fence, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + char const * const filename, + VkImage * outImage, + VkDeviceMemory * outMemory, + VkImageView * outImageView) +{ + uint32_t imageSize; + void const * imageStart = file::open(filename, &imageSize); + void * imageData; + uint32_t imageDataSize; + tga::header const * tga = tga::validate(imageStart, imageSize, &imageData, &imageDataSize); + + VkFormat format = VK_FORMAT_B8G8R8A8_UNORM; + uint32_t width = tga->image.width; + uint32_t height = tga->image.height; + uint32_t levelCount = 1; + uint32_t levelOffset = 0; + + createImage(device, + nonCoherentAtomSize, + physicalDeviceMemoryProperties, + format, + width, + height, + levelCount, + outImage, + outMemory, + outImageView); + + textureTransfer(device, + queue, + commandBuffer, + fence, + nonCoherentAtomSize, + physicalDeviceMemoryProperties, + imageDataSize, + imageData, + *outImage, + width, + height, + levelCount, + &levelOffset); + + // imageData is not malloc'ed, it is a pointer to file:: data, which is also not malloc'ed +}