diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47f160c..3cef7ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ - + SET(PLUMAGE_RENDER "${CMAKE_CURRENT_SOURCE_DIR}/render") message("working path ${PLUMAGE_RENDER}") @@ -11,6 +11,18 @@ set(MAIN_FILE set(GLTF_MODEL_LOADER "${PLUMAGE_RENDER}/glTFBoundingBox.h" "${PLUMAGE_RENDER}/glTFBoundingBox.cpp" + "${PLUMAGE_RENDER}/glTFTexture.h" + "${PLUMAGE_RENDER}/glTFTexture.cpp" + "render/glTFModel.h" + "render/glTFModel.cpp" +) + +set(VULKAN_BASE + "render/VulkanBase_Common.h" + "render/VulkanDevice.h" + "render/VulkanDevice.cpp" + "render/VulkanTextureSampler.h" + "render/VulkanTextureSampler.cpp" ) # wayland requires additional source files @@ -27,6 +39,8 @@ ENDIF() # Add optional readme / tutorial #file(GLOB README_FILES "${HOMEWORK_FOLDER}/*.md") + + include_directories(${3rdParty_gli_path}) include_directories(${3rdParty_glm_path}) include_directories(${3rdParty_imgui_path}) @@ -43,10 +57,11 @@ if(WIN32) add_executable(${RenderName} WIN32 ${SHADERS_GLSL} ${SHADERS_HLSL} ${MAIN_FILE} ${GLTF_MODEL_LOADER} - "render/glTFModel.h" - "render/glTFModel.cpp" + ${VULKAN_BASE} + "render/renderFoundation.h" - "render/renderFoundation.cpp" ) + "render/renderFoundation.cpp" +) target_link_libraries(${RenderName} base ${Vulkan_LIBRARY} ${WINLIBS}) endif() set_target_properties(${RenderName} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) @@ -56,13 +71,14 @@ if(RESOURCE_INSTALL_DIR) install(TARGETS ${RenderName} DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() - -#if(WIN32) -# target_compile_definitions(MyLib PRIVATE MYLIB_EXPORTS) -#endif(UNIX AND NOT APPLE) # Linux及类似系统 -# add_compile_options(-fvisibility=hidden) -# add_compile_options(-fvisibility-inlines-hidden) -#endif() +# 后续移动到动态库后删除 +if(WIN32) + target_compile_definitions(${RenderName} PRIVATE VULKANBASE_STATIC_BUILD) + target_compile_definitions(${RenderName} PRIVATE GLTFLOADER_STATIC_BUILD) +elseif(UNIX AND NOT APPLE) # Linux及类似系统 + add_compile_options(-fvisibility=hidden) + add_compile_options(-fvisibility-inlines-hidden) +endif() diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 04dbb81..0c7ee8d 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB BASE_SRC "*.cpp" "*.h" "${3rdParty_imgui_path}/*.cpp" ) +file(GLOB BASE_SRC "*.cpp" "*.h" "${3rdParty_imgui_path}/*.cpp" ) include_directories(${3rdParty_ktx_path}) diff --git a/src/base/VulkanDevice.hpp b/src/base/VulkanDevice.hpp index e9b45c5..3ec171d 100644 --- a/src/base/VulkanDevice.hpp +++ b/src/base/VulkanDevice.hpp @@ -1,4 +1,4 @@ -/* +/* * Vulkan device class * * Encapsulates a physical Vulkan device and it's logical representation @@ -27,6 +27,8 @@ #include "VulkanTools.h" + +/// @brief 已单独剥离,等待删除 namespace vks { struct VulkanDevice diff --git a/src/base/VulkanTools.h b/src/base/VulkanTools.h index b7f55bc..a5ed699 100644 --- a/src/base/VulkanTools.h +++ b/src/base/VulkanTools.h @@ -1,4 +1,4 @@ - + #pragma once #include "vulkan/vulkan.h" @@ -49,7 +49,7 @@ { \ std::cout << "Fatal : VkResult is \"" << vks::tools::errorString(res) << "\" in " << __FILE__ << " at line " << __LINE__ << "\n"; \ assert(res == VK_SUCCESS); \ - } \ + } \ } #endif diff --git a/src/render/VulkanBase_Common.h b/src/render/VulkanBase_Common.h new file mode 100644 index 0000000..d36a557 --- /dev/null +++ b/src/render/VulkanBase_Common.h @@ -0,0 +1,30 @@ +#pragma once + +/// 命名空间宏 +#define VULKANBASE_NAMESPACE_BEGIN namespace VulkanBase { +#define VULKANBASE_NAMESPACE_END } + +/// windows 导入导出宏,GLTFLOADER_EXPORTS将在cmake中定义 +/// unix-like 下提供符号可见性控制 +#ifdef VULKANBASE_STATIC_BUILD +#define VULKANBASE_API +#define VULKANBASE_LOCAL +#else +#ifdef _WIN32 +#ifdef VULKANBASE_EXPORTS +#define VULKANBASE_API __declspec(dllexport) +#define VULKANBASE_LOCAL +#else +#define VULKANBASE_API __declspec(dllimport) +#define VULKANBASE_LOCAL +#endif +#else +#if __GNUC__ >= 4 || defined(__clang__) +#define VULKANBASE_API __attribute__ ((visibility ("default"))) +#define VULKANBASE_LOCAL __attribute__ ((visibility ("hidden"))) +#else +#define VULKANBASE_API +#define VULKANBASE_LOCAL +#endif +#endif +#endif \ No newline at end of file diff --git a/src/render/VulkanDevice.cpp b/src/render/VulkanDevice.cpp new file mode 100644 index 0000000..ba6ed06 --- /dev/null +++ b/src/render/VulkanDevice.cpp @@ -0,0 +1,213 @@ +#include "VulkanDevice.h" +#include +#include +#include + +VULKANBASE_NAMESPACE_BEGIN + +VulkanDevice::VulkanDevice() + :m_commandPool(VK_NULL_HANDLE) +{ + +} + +VulkanDevice::VulkanDevice(VkPhysicalDevice physicalDevice) +{ + /// 检查物理设备是否存在,后续使用log方式记录 + assert(physicalDevice); + m_physicalDevice = physicalDevice; + + /// 获取设备信息 + vkGetPhysicalDeviceProperties(physicalDevice, &m_properties); + /// 获取设备支持的功能 + vkGetPhysicalDeviceFeatures(physicalDevice, &m_features); + /// 获取设备内存信息,用于创建内存 + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &m_memoryProperties); + /// 队列族信息,用于设备创建时获取设置获取的队列 + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + /// 检查设备队列族数量,必须大于0 + assert(queueFamilyCount > 0); + m_queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, m_queueFamilyProperties.data()); +} + + +VulkanDevice::~VulkanDevice() +{ + if (m_commandPool) { + vkDestroyCommandPool(m_logicalDevice, m_commandPool, nullptr); + } + if (m_logicalDevice) { + vkDestroyDevice(m_logicalDevice, nullptr); + } +} + +VkDevice VulkanDevice::getLogicalDevice() +{ + return m_logicalDevice; +} + +VkPhysicalDevice VulkanDevice::getPhysicalDevice() +{ + return m_physicalDevice; +} + +uint32_t VulkanDevice::getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32* memTypeFound = nullptr) +{ + for (uint32_t i = 0; i < m_memoryProperties.memoryTypeCount; i++) { + if ((typeBits & 1) == 1) { + if ((m_memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { + if (memTypeFound) { + *memTypeFound = true; + } + return i; + } + } + typeBits >>= 1; + } + + if (memTypeFound) { + *memTypeFound = false; + return 0; + } + else { + throw std::runtime_error("Could not find a matching memory type"); + } +} + +uint32_t VulkanDevice::getQueueFamilyIndex(VkQueueFlagBits queueFlags) +{ + /// 获取支持计算的队列组索引 + if (queueFlags & VK_QUEUE_COMPUTE_BIT) + { + for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) { + if ((m_queueFamilyProperties[i].queueFlags & queueFlags) && ((m_queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { + return i; + break; + } + } + } + + /// 对于其他的队列类型,如果当前没有单独的计算队列,返回支持标志类型的第一个队列 + for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) { + if (m_queueFamilyProperties[i].queueFlags & queueFlags) { + return i; + break; + } + } + + throw std::runtime_error("Could not find a matching queue family index"); +} + +VkResult VulkanDevice::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void* data = nullptr) +{ + // 创建buffer句柄 + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.usage = usageFlags; + bufferCreateInfo.size = size; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(m_logicalDevice, &bufferCreateInfo, nullptr, buffer)); + + // 创建buffer的设备内存分配信息 + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc{}; + memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vkGetBufferMemoryRequirements(m_logicalDevice, *buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + /// 获取符合buffer的设备内存类型索引 + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(m_logicalDevice, &memAlloc, nullptr, memory)); + + // 如何buffer指针已经存在,复制或者映射该buffer + if (data != nullptr) + { + void* mapped; + VK_CHECK_RESULT(vkMapMemory(m_logicalDevice, *memory, 0, size, 0, &mapped)); + memcpy(mapped, data, size); + // 如果host coherency 未设置, 对buffer进行flush,让设备侧可见 + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + { + VkMappedMemoryRange mappedRange{}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = *memory; + mappedRange.offset = 0; + mappedRange.size = size; + vkFlushMappedMemoryRanges(m_logicalDevice, 1, &mappedRange); + } + vkUnmapMemory(m_logicalDevice, *memory); + } + + // 绑定设备内存到buffer object + VK_CHECK_RESULT(vkBindBufferMemory(m_logicalDevice, *buffer, *memory, 0)); + + return VK_SUCCESS; +} + +VkCommandPool VulkanDevice::createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) +{ + /// 创建命令缓冲池 + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; + cmdPoolInfo.flags = createFlags; + VkCommandPool cmdPool; + VK_CHECK_RESULT(vkCreateCommandPool(m_logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); + return cmdPool; +} + +VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, bool begin = false) +{ + VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; + cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdBufAllocateInfo.commandPool = m_commandPool; + cmdBufAllocateInfo.level = level; + cmdBufAllocateInfo.commandBufferCount = 1; + + VkCommandBuffer cmdBuffer; + VK_CHECK_RESULT(vkAllocateCommandBuffers(m_logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); + + // 开始记录指令buffer + if (begin) { + beginCommandBuffer(cmdBuffer); + } + + return cmdBuffer; +} + +void VulkanDevice::beginCommandBuffer(VkCommandBuffer commandBuffer) +{ + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBI)); +} + +void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) +{ + VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + //创建同步栅栏,确保命令buffer执行完毕 + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &fence)); + + // 提交队列 + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + // 等待栅栏发出信号说明命令buffer执行完毕 + VK_CHECK_RESULT(vkWaitForFences(m_logicalDevice, 1, &fence, VK_TRUE, 100000000000)); + // 同步栅栏使命结束,销毁 + vkDestroyFence(m_logicalDevice, fence, nullptr); + // 需要的时候,销毁命令buffer + if (free) { + vkFreeCommandBuffers(m_logicalDevice, m_commandPool, 1, &commandBuffer); + } +} + +VULKANBASE_NAMESPACE_END \ No newline at end of file diff --git a/src/render/VulkanDevice.h b/src/render/VulkanDevice.h new file mode 100644 index 0000000..be86484 --- /dev/null +++ b/src/render/VulkanDevice.h @@ -0,0 +1,97 @@ +#ifndef VULKANDEVICE_H +#define VULKANDEVICE_H + +#include "VulkanBase_Common.h" + +#include +#include + +VULKANBASE_NAMESPACE_BEGIN + +/// @brief vulkan的设备类 +class VulkanDevice +{ +/// @brief 构造和setter/getter +public: + VulkanDevice(); + VulkanDevice(VkPhysicalDevice physicalDevice); + ~VulkanDevice(); + + VkDevice getLogicalDevice(); + VkPhysicalDevice getPhysicalDevice(); + +public: + /// @brief 获取已设置所有请求属性位的设备内存类型的索引 + /// @param typeBits 请求的每种设备内存类型设置的位掩码,通常通过VkMemoryRequirements获取 + /// @param properties 要请求的设备内存类型的属性位掩码 + /// @param memTypeFound 如果符合的设备内存类型存在,则该指针的布尔值为true + /// @return 请求的设备内存类型的索引 + /// @throw 如果 memTypeFound 为空且找不到支持需要的属性的设备内存类型,则抛出异常 + uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32* memTypeFound = nullptr); + /// @brief 获取支持所请求队列标志的队列族的索引 + /// @param queueFlags 用于查找队列族索引的队列标志 + /// @return 与标志匹配的队列族索引 + /// @throw 如果找不到支持所请求标志的队列族索引,则抛出异常 + uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags); + /// @brief 据分配的物理设备创建逻辑设备,同时获取默认队列族索引 + /// @param enabledFeatures 在创建设备时启用某些功能 + /// @param enabledExtensions 在创建设备时启用某些扩展 + /// @param requestedQueueTypes 指定要从设备请求的队列类型 + /// @return 逻辑设备是否成功创建 + VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + /// @brief 在设备上创建缓冲区 + /// @param usageFlags 缓冲区的使用标志位掩码(即索引、顶点、统一缓冲区) + /// @param memoryPropertyFlags 此缓冲区的内存属性(即设备本地、主机可见、一致) + /// @param size 缓冲区的大小(以字节为单位) + /// @param buffer 指向函数获取的缓冲区句柄的指针 + /// @param memory 指向函数获取的设备内存句柄的指针 + /// @param data 指向创建后应复制到缓冲区的数据的指针(可选,如果未设置,则不会复制任何数据) + /// @return 如果已创建缓冲区句柄和设备内存并且已复制数据(可选传递),则返回 VK_SUCCESS + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void* data = nullptr); + /// @brief 创建命令池以分配命令缓冲区 + /// @param queueFamilyIndex 要为其创建命令池的队列的系列索引 + /// @param createFlags 可选)命令池创建标志(默认为 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + /// @return 创建的命令缓冲区的句柄 + VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + /// @brief 从命令池中分配命令缓冲区 + /// @param level 新命令缓冲区的级别(主或次) + /// @param begin 为 true时开始在新命令缓冲区上进行记录 + /// @return 分配的命令缓冲区的句柄 + VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false); + /// @brief 开始在指定命令缓冲区记录 + /// @param commandBuffer + void beginCommandBuffer(VkCommandBuffer commandBuffer); + /// @brief 停止指定命令缓冲区记录并将其提交到队列,使用栅栏来确保命令缓冲区已完成执行 + /// @param commandBuffer 要刷新的命令缓冲区 + /// @param queue 要将命令缓冲区提交到的队列 + /// @param free 提交后释放命令缓冲区(默认为 true) + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true); + +private: + + VkPhysicalDevice m_physicalDevice; + VkDevice m_logicalDevice; + VkPhysicalDeviceProperties m_properties; + VkPhysicalDeviceFeatures m_features; + VkPhysicalDeviceFeatures m_enabledFeatures; + VkPhysicalDeviceMemoryProperties m_memoryProperties; + std::vector m_queueFamilyProperties; + VkCommandPool m_commandPool; + + struct { + uint32_t graphics; + uint32_t compute; + } m_queueFamilyIndices; +}; + + + + + +VULKANBASE_NAMESPACE_END + + + + + +#endif // !VULKANDEVICE_H diff --git a/src/render/VulkanTextureSampler.cpp b/src/render/VulkanTextureSampler.cpp new file mode 100644 index 0000000..08c8f72 --- /dev/null +++ b/src/render/VulkanTextureSampler.cpp @@ -0,0 +1,68 @@ +#include "VulkanTextureSampler.h" + +VULKANBASE_NAMESPACE_BEGIN + +VulkanTextureSampler::VulkanTextureSampler() +{ +} + +VulkanTextureSampler::~VulkanTextureSampler() +{ +} + +VkFilter VulkanTextureSampler::getMaxFilter() +{ + return m_maxFilter; +} + +void VulkanTextureSampler::setMaxFilter(VkFilter maxFilter) +{ + m_maxFilter = maxFilter; +} + +VkFilter VulkanTextureSampler::getMinFilter() +{ + return m_minFilter; +} + +void VulkanTextureSampler::setMinFilter(VkFilter minFilter) +{ + m_minFilter = minFilter; +} + +VkSamplerAddressMode VulkanTextureSampler::getAddressModeU() +{ + return m_addressModeU; +} + +void VulkanTextureSampler::setAddressModeU(VkSamplerAddressMode samplerAddresMode) +{ + m_addressModeU = samplerAddresMode; +} + +VkSamplerAddressMode VulkanTextureSampler::getAddressModeV() +{ + return m_addressModeV; +} + +void VulkanTextureSampler::setAddressModeV(VkSamplerAddressMode samplerAddresMode) +{ + m_addressModeV = samplerAddresMode; +} + +VkSamplerAddressMode VulkanTextureSampler::getAddressModeW() +{ + return m_addressModeW; +} + +void VulkanTextureSampler::setAddressModeW(VkSamplerAddressMode samplerAddresMode) +{ + m_addressModeW = samplerAddresMode; +} + + + + + + +VULKANBASE_NAMESPACE_END \ No newline at end of file diff --git a/src/render/VulkanTextureSampler.h b/src/render/VulkanTextureSampler.h new file mode 100644 index 0000000..7eb6956 --- /dev/null +++ b/src/render/VulkanTextureSampler.h @@ -0,0 +1,45 @@ +#ifndef VULKANTEXTURESAMPLER_H +#define VULKANTEXTURESAMPLER_H + +#include "VulkanBase_Common.h" + +#include + +VULKANBASE_NAMESPACE_BEGIN + + +/// @brief 贴图采样器,暂时适用于gltf模型贴图 +class VulkanTextureSampler +{ +public: + VulkanTextureSampler(); + ~VulkanTextureSampler(); + + VkFilter getMaxFilter(); + void setMaxFilter(VkFilter maxFilter); + + VkFilter getMinFilter(); + void setMinFilter(VkFilter minFilter); + + VkSamplerAddressMode getAddressModeU(); + void setAddressModeU(VkSamplerAddressMode samplerAddresMode); + + VkSamplerAddressMode getAddressModeV(); + void setAddressModeV(VkSamplerAddressMode samplerAddresMode); + + VkSamplerAddressMode getAddressModeW(); + void setAddressModeW(VkSamplerAddressMode samplerAddresMode); + +private: + + VkFilter m_maxFilter; + VkFilter m_minFilter; + VkSamplerAddressMode m_addressModeU; + VkSamplerAddressMode m_addressModeV; + VkSamplerAddressMode m_addressModeW; +}; + +VULKANBASE_NAMESPACE_END + + +#endif // !VULKANTEXTURESAMPLER_H diff --git a/src/render/glTFBoundingBox.cpp b/src/render/glTFBoundingBox.cpp index 3c31dc6..425e71f 100644 --- a/src/render/glTFBoundingBox.cpp +++ b/src/render/glTFBoundingBox.cpp @@ -1,4 +1,4 @@ -#include "glTFBoundingBox.h" +#include "glTFBoundingBox.h" GLTFLOADER_NAMESPACE_BEGIN diff --git a/src/render/glTFTexture.cpp b/src/render/glTFTexture.cpp new file mode 100644 index 0000000..068b33f --- /dev/null +++ b/src/render/glTFTexture.cpp @@ -0,0 +1,270 @@ +#include "glTFTexture.h" +#include + +GLTFLOADER_NAMESPACE_BEGIN + +glTFTexture::glTFTexture() +{ +} + +glTFTexture::~glTFTexture() +{ +} + +void glTFTexture::updateDescriptor() +{ + m_descriptor.sampler = m_sampler; + m_descriptor.imageView = m_view; + m_descriptor.imageLayout = m_imageLayout; +} + +void glTFTexture::destroy() +{ + VkDevice logicalDevice = m_device->getLogicalDevice(); + vkDestroyImageView(logicalDevice, m_view, nullptr); + vkDestroyImage(logicalDevice, m_image, nullptr); + vkFreeMemory(logicalDevice, m_deviceMemory, nullptr); + vkDestroySampler(logicalDevice, m_sampler, nullptr); + +} + +void glTFTexture::fromglTfImage(tinygltf::Image& gltfimage, VulkanBase::VulkanTextureSampler textureSampler, VulkanBase::VulkanDevice* device, VkQueue copyQueue) +{ + VkDevice logicalDevice = device->getLogicalDevice(); + unsigned char* buffer = nullptr; + VkDeviceSize bufferSize = 0; + bool deleteBuffer = false; + if (gltfimage.component == 3) { + /// 即使创建为24bit的rgb,硬件上依旧是rgba,因此填充alpha值,但带宽压力上升25% + /// 可选todo:采用计算着色器将rgb写入uniform buffer + bufferSize = gltfimage.width * gltfimage.height * 4; + buffer = new unsigned char[bufferSize]; + unsigned char* rgba = buffer; + unsigned char* rgb = &gltfimage.image[0]; + for (int32_t i = 0; i < gltfimage.width * gltfimage.height; ++i) { + for (int32_t j = 0; j < 3; ++j) { + rgba[j] = rgb[j]; + } + rgba += 4; + rgb += 3; + } + deleteBuffer = true; + } + else { + buffer = &gltfimage.image[0]; + bufferSize = gltfimage.image.size(); + } + + VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + + VkFormatProperties formatProperties; + + m_width = gltfimage.width; + m_height = gltfimage.height; + m_mipLevels = static_cast(floor(log2(std::max(m_width, m_height))) + 1.0); + + vkGetPhysicalDeviceFormatProperties(device->getPhysicalDevice(), format, &formatProperties); + assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); + assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT); + /// 分配存放缓冲区的设备内存 + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + VkMemoryRequirements memReqs{}; + + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0)); + // 向设备内存中写入缓冲区 + uint8_t* data; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data)); + memcpy(data, buffer, bufferSize); + vkUnmapMemory(logicalDevice, stagingMemory); + /// 创建图片类型设备内存 + VkImageCreateInfo imageCreateInfo{}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = m_mipLevels; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = { m_width, m_height, 1 }; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &m_image)); + vkGetImageMemoryRequirements(logicalDevice, m_image, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &m_deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, m_image, m_deviceMemory, 0)); + /// 分配复制命令缓冲区 + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.levelCount = 1; + subresourceRange.layerCount = 1; + + { + /// 插入barrier,强制管线等待上方指令完成 + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.image = m_image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = m_width; + bufferCopyRegion.imageExtent.height = m_height; + bufferCopyRegion.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(copyCmd, stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); + + { + /// 插入barrier,管线等待copy操作完成 + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = m_image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + /// 提交到命令队列 + device->flushCommandBuffer(copyCmd, copyQueue, true); + /// 销毁图片buffer存储的设备内存 + vkFreeMemory(logicalDevice, stagingMemory, nullptr); + /// 销毁图片buffer存储的设备缓冲区 + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + + // 生成mip-map链 (glTF 使用 jpg 和 png 格式, 需要当场生成mip-map) + VkCommandBuffer blitCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + for (uint32_t i = 1; i < m_mipLevels; i++) { + VkImageBlit imageBlit{}; + + imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlit.srcSubresource.layerCount = 1; + imageBlit.srcSubresource.mipLevel = i - 1; + imageBlit.srcOffsets[1].x = int32_t(m_width >> (i - 1)); + imageBlit.srcOffsets[1].y = int32_t(m_height >> (i - 1)); + imageBlit.srcOffsets[1].z = 1; + + imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlit.dstSubresource.layerCount = 1; + imageBlit.dstSubresource.mipLevel = i; + imageBlit.dstOffsets[1].x = int32_t(m_width >> i); + imageBlit.dstOffsets[1].y = int32_t(m_height >> i); + imageBlit.dstOffsets[1].z = 1; + + VkImageSubresourceRange mipSubRange = {}; + mipSubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + mipSubRange.baseMipLevel = i; + mipSubRange.levelCount = 1; + mipSubRange.layerCount = 1; + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.image = m_image; + imageMemoryBarrier.subresourceRange = mipSubRange; + vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + vkCmdBlitImage(blitCmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlit, VK_FILTER_LINEAR); + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = m_image; + imageMemoryBarrier.subresourceRange = mipSubRange; + vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + } + + subresourceRange.levelCount = m_mipLevels; + m_imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = m_image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + m_device->flushCommandBuffer(blitCmd, copyQueue, true); + + /// 创建贴图采样器 + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = textureSampler.getMaxFilter(); + samplerInfo.minFilter = textureSampler.getMinFilter(); + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.addressModeU = textureSampler.getAddressModeU(); + samplerInfo.addressModeV = textureSampler.getAddressModeV(); + samplerInfo.addressModeW = textureSampler.getAddressModeW(); + samplerInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + samplerInfo.maxAnisotropy = 1.0; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxLod = (float)m_mipLevels; + samplerInfo.maxAnisotropy = 8.0f; + samplerInfo.anisotropyEnable = VK_TRUE; + VK_CHECK_RESULT(vkCreateSampler(logicalDevice, &samplerInfo, nullptr, &m_sampler)); + + /// 创建图片的数据视图,用于读写图片 + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = m_image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = format; + viewInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.levelCount = m_mipLevels; + VK_CHECK_RESULT(vkCreateImageView(logicalDevice, &viewInfo, nullptr, &m_view)); + + updateDescriptor(); + + if (deleteBuffer) + delete[] buffer; + +} + +GLTFLOADER_NAMESPACE_END \ No newline at end of file diff --git a/src/render/glTFTexture.h b/src/render/glTFTexture.h new file mode 100644 index 0000000..e4f6953 --- /dev/null +++ b/src/render/glTFTexture.h @@ -0,0 +1,50 @@ +#ifndef GLTFTEXTURE_H +#define GLTFTEXTURE_H + +#include "glTFModel_Common.h" + +#include "VulkanDevice.h" +#include "VulkanTextureSampler.h" + +#include + + +GLTFLOADER_NAMESPACE_BEGIN + +/// @brief gltf模型的贴图 +class GLTFLOADER_API glTFTexture +{ +public: + glTFTexture(); + ~glTFTexture(); + + void updateDescriptor(); + + void destroy(); + + void fromglTfImage(tinygltf::Image& gltfimage, VulkanBase::VulkanTextureSampler textureSampler, VulkanBase::VulkanDevice* device, VkQueue copyQueue); + +private: + + VulkanBase::VulkanDevice* m_device; + VkImage m_image; + VkImageLayout m_imageLayout; + VkDeviceMemory m_deviceMemory; + VkImageView m_view; + uint32_t m_width, m_height; + uint32_t m_mipLevels; + uint32_t m_layerCount; + VkDescriptorImageInfo m_descriptor; + VkSampler m_sampler; +}; + + + + + +GLTFLOADER_NAMESPACE_END + + + + +#endif // !GLTFTEXTURE_H