/* Copyright (c) 2019-2025, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 the "License"; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Texture loading (and display) example (including mip maps) */ #include "texture_loading.h" TextureLoading::TextureLoading() { zoom = -1.f; rotation = { 0.0f, 15.0f, 0.0f }; title = "Texture loading"; this_instance = this; } TextureLoading::~TextureLoading() { if (has_device()) { // Clean up used Vulkan resources // Note : Inherited destructor cleans up resources stored in base class vkDestroyPipeline(get_device().get_handle(), pipelines.solid, nullptr); vkDestroyPipeline(get_device().get_handle(), pipelines.background, nullptr); vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr); vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout_bg, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout_bg, nullptr); if (point_pipeline) { vkDestroyPipeline(get_device().get_handle(), point_pipeline, nullptr); } if (point_pipeline_layout) { vkDestroyPipelineLayout(get_device().get_handle(), point_pipeline_layout, nullptr); } if (point_descriptor_set_layout) { vkDestroyDescriptorSetLayout(get_device().get_handle(), point_descriptor_set_layout, nullptr); } } destroy_texture(texture); destroy_texture(cam_text); vertex_buffer.reset(); index_buffer.reset(); uniform_buffer_vs.reset(); point_vertex_buffer.reset(); // BufferC 会自动清理 //stop(); } // Enable physical device features required for this example void TextureLoading::request_gpu_features(vkb::PhysicalDevice& gpu) { // Enable anisotropic filtering if supported if (gpu.get_features().samplerAnisotropy) { gpu.get_mutable_requested_features().samplerAnisotropy = VK_TRUE; } } void hslToRgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b) { float c = (1 - std::abs(2 * l - 1)) * s; float x = c * (1 - std::abs(std::fmod(h / 60.0f, 2.0f) - 1)); float m = l - c / 2.0f; float r_, g_, b_; if (h < 60) { r_ = c; g_ = x; b_ = 0; } else if (h < 120) { r_ = x; g_ = c; b_ = 0; } else if (h < 180) { r_ = 0; g_ = c; b_ = x; } else if (h < 240) { r_ = 0; g_ = x; b_ = c; } else if (h < 300) { r_ = x; g_ = 0; b_ = c; } else { r_ = c; g_ = 0; b_ = x; } r = static_cast((r_ + m) * 255); g = static_cast((g_ + m) * 255); b = static_cast((b_ + m) * 255); } std::vector generateSimpleTestImage(int width, int height, int cell_width) { std::vector imageData(width * height * 4); int gridCols = (width + cell_width - 1) / cell_width; int gridRows = (height + cell_width - 1) / cell_width; // 存储每个单元格的颜色 std::vector>> cellColors( gridRows, std::vector>( gridCols, std::vector(4) ) ); // 为每个单元格生成不同的颜色 for (int gridY = 0; gridY < gridRows; ++gridY) { for (int gridX = 0; gridX < gridCols; ++gridX) { // 使用网格坐标生成HSL颜色 float hue = static_cast(gridX + gridY * gridCols) / (gridCols * gridRows) * 360.0f; float saturation = 0.7f + 0.3f * static_cast(gridX % 2); // 交替饱和度 float lightness = 0.5f + 0.2f * static_cast(gridY % 2); // 交替亮度 uint8_t r, g, b; hslToRgb(hue, saturation, lightness, r, g, b); cellColors[gridY][gridX][0] = r; cellColors[gridY][gridX][1] = g; cellColors[gridY][gridX][2] = b; cellColors[gridY][gridX][3] = 255; if (gridY == 0 && gridX == 0) { cellColors[gridY][gridX][0] = 0; cellColors[gridY][gridX][1] = 0; cellColors[gridY][gridX][2] = 0; cellColors[gridY][gridX][3] = 255; } if (gridY == 0 && gridX == gridCols-1) { cellColors[gridY][gridX][0] = 255; cellColors[gridY][gridX][1] = 0; cellColors[gridY][gridX][2] = 0; cellColors[gridY][gridX][3] = 255; } if (gridY == gridRows-1 && gridX == 0) { cellColors[gridY][gridX][0] = 0; cellColors[gridY][gridX][1] = 0; cellColors[gridY][gridX][2] = 255; cellColors[gridY][gridX][3] = 255; } if (gridY == gridRows - 1 && gridX == gridCols - 1) { cellColors[gridY][gridX][0] = 255; cellColors[gridY][gridX][1] = 255; cellColors[gridY][gridX][2] = 255; cellColors[gridY][gridX][3] = 255; } } } // 填充像素数据 for (int y = 0; y < height; ++y) { int gridY = y / cell_width; for (int x = 0; x < width; ++x) { int gridX = x / cell_width; if (gridY < gridRows && gridX < gridCols) { const uint8_t* color = cellColors[gridY][gridX].data(); int index = (y * width + x) * 4; imageData[index] = color[0]; imageData[index + 1] = color[1]; imageData[index + 2] = color[2]; imageData[index + 3] = color[3]; } } } return imageData; } void TextureLoading::load_texture() { // We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) std::string filename = vkb::fs::path::get(vkb::fs::path::Assets, "textures/metalplate01_rgba.ktx"); // ktx1 doesn't know whether the content is sRGB or linear, but most tools save in sRGB, so assume that. VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; ktxTexture* ktx_texture; KTX_error_code result; result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktx_texture); if (ktx_texture == nullptr) { throw std::runtime_error("Couldn't load texture"); } // assert(!tex2D.empty()); texture.width = ktx_texture->baseWidth; texture.height = ktx_texture->baseHeight; texture.mip_levels = ktx_texture->numLevels; // We prefer using staging to copy the texture data to a device local optimal image VkBool32 use_staging = true; // Only use linear tiling if forced bool force_linear_tiling = false; if (force_linear_tiling) { // Don't use linear if format is not supported for (linear) shader sampling // Get device properties for the requested texture format VkFormatProperties format_properties; vkGetPhysicalDeviceFormatProperties(get_device().get_gpu().get_handle(), format, &format_properties); use_staging = !(format_properties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); } VkMemoryAllocateInfo memory_allocate_info = vkb::initializers::memory_allocate_info(); VkMemoryRequirements memory_requirements = {}; ktx_uint8_t* ktx_image_data = ktx_texture->pData; ktx_size_t ktx_texture_size = ktx_texture->dataSize; if (use_staging) { // Copy data to an optimal tiled image // This loads the texture data into a host local buffer that is copied to the optimal tiled image on the device // Create a host-visible staging buffer that contains the raw image data // This buffer will be the data source for copying texture data to the optimal tiled image on the device VkBuffer staging_buffer; VkDeviceMemory staging_memory; VkBufferCreateInfo buffer_create_info = vkb::initializers::buffer_create_info(); buffer_create_info.size = ktx_texture_size; // This buffer is used as a transfer source for the buffer copy buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK(vkCreateBuffer(get_device().get_handle(), &buffer_create_info, nullptr, &staging_buffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(get_device().get_handle(), staging_buffer, &memory_requirements); memory_allocate_info.allocationSize = memory_requirements.size; // Get memory type index for a host visible buffer memory_allocate_info.memoryTypeIndex = get_device().get_gpu().get_memory_type(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK(vkAllocateMemory(get_device().get_handle(), &memory_allocate_info, nullptr, &staging_memory)); VK_CHECK(vkBindBufferMemory(get_device().get_handle(), staging_buffer, staging_memory, 0)); // Copy texture data into host local staging buffer uint8_t* data; VK_CHECK(vkMapMemory(get_device().get_handle(), staging_memory, 0, memory_requirements.size, 0, (void**)&data)); memcpy(data, ktx_image_data, ktx_texture_size); vkUnmapMemory(get_device().get_handle(), staging_memory); // Setup buffer copy regions for each mip level std::vector buffer_copy_regions; for (uint32_t i = 0; i < texture.mip_levels; i++) { ktx_size_t offset; KTX_error_code result = ktxTexture_GetImageOffset(ktx_texture, i, 0, 0, &offset); VkBufferImageCopy buffer_copy_region = {}; buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; buffer_copy_region.imageSubresource.mipLevel = i; buffer_copy_region.imageSubresource.baseArrayLayer = 0; buffer_copy_region.imageSubresource.layerCount = 1; buffer_copy_region.imageExtent.width = ktx_texture->baseWidth >> i; buffer_copy_region.imageExtent.height = ktx_texture->baseHeight >> i; buffer_copy_region.imageExtent.depth = 1; buffer_copy_region.bufferOffset = offset; buffer_copy_regions.push_back(buffer_copy_region); } // Create optimal tiled target image on the device VkImageCreateInfo image_create_info = vkb::initializers::image_create_info(); image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = format; image_create_info.mipLevels = texture.mip_levels; image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // Set initial layout of the image to undefined image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.extent = { texture.width, texture.height, 1 }; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VK_CHECK(vkCreateImage(get_device().get_handle(), &image_create_info, nullptr, &texture.image)); vkGetImageMemoryRequirements(get_device().get_handle(), texture.image, &memory_requirements); memory_allocate_info.allocationSize = memory_requirements.size; memory_allocate_info.memoryTypeIndex = get_device().get_gpu().get_memory_type(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK(vkAllocateMemory(get_device().get_handle(), &memory_allocate_info, nullptr, &texture.device_memory)); VK_CHECK(vkBindImageMemory(get_device().get_handle(), texture.image, texture.device_memory, 0)); VkCommandBuffer copy_command = get_device().create_command_buffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Image memory barriers for the texture image // The sub resource range describes the regions of the image that will be transitioned using the memory barriers below VkImageSubresourceRange subresource_range = {}; // Image only contains color data subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // Start at first mip level subresource_range.baseMipLevel = 0; // We will transition on all mip levels subresource_range.levelCount = texture.mip_levels; // The 2D texture only has one layer subresource_range.layerCount = 1; // Transition the texture image layout to transfer target, so we can safely copy our buffer data to it. VkImageMemoryBarrier image_memory_barrier = vkb::initializers::image_memory_barrier(); image_memory_barrier.image = texture.image; image_memory_barrier.subresourceRange = subresource_range; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; // Insert a memory dependency at the proper pipeline stages that will execute the image layout transition // Source pipeline stage is host write/read execution (VK_PIPELINE_STAGE_HOST_BIT) // Destination pipeline stage is copy command execution (VK_PIPELINE_STAGE_TRANSFER_BIT) vkCmdPipelineBarrier( copy_command, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copy_command, staging_buffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast(buffer_copy_regions.size()), buffer_copy_regions.data()); // Once the data has been uploaded we transfer to the texture image to the shader read layout, so it can be sampled from image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Insert a memory dependency at the proper pipeline stages that will execute the image layout transition // Source pipeline stage stage is copy command execution (VK_PIPELINE_STAGE_TRANSFER_BIT) // Destination pipeline stage fragment shader access (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) vkCmdPipelineBarrier( copy_command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); // Store current layout for later reuse texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; get_device().flush_command_buffer(copy_command, queue, true); // Clean up staging resources vkDestroyBuffer(get_device().get_handle(), staging_buffer, nullptr); vkFreeMemory(get_device().get_handle(), staging_memory, nullptr); } else { // Copy data to a linear tiled image VkImage mappable_image; VkDeviceMemory mappable_memory; // Load mip map level 0 to linear tiling image VkImageCreateInfo image_create_info = vkb::initializers::image_create_info(); image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = format; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; image_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; image_create_info.extent = { texture.width, texture.height, 1 }; VK_CHECK(vkCreateImage(get_device().get_handle(), &image_create_info, nullptr, &mappable_image)); // Get memory requirements for this image like size and alignment vkGetImageMemoryRequirements(get_device().get_handle(), mappable_image, &memory_requirements); // Set memory allocation size to required memory size memory_allocate_info.allocationSize = memory_requirements.size; // Get memory type that can be mapped to host memory memory_allocate_info.memoryTypeIndex = get_device().get_gpu().get_memory_type(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK(vkAllocateMemory(get_device().get_handle(), &memory_allocate_info, nullptr, &mappable_memory)); VK_CHECK(vkBindImageMemory(get_device().get_handle(), mappable_image, mappable_memory, 0)); // Map image memory void* data; ktx_size_t ktx_image_size = ktxTexture_GetImageSize(ktx_texture, 0); VK_CHECK(vkMapMemory(get_device().get_handle(), mappable_memory, 0, memory_requirements.size, 0, &data)); // Copy image data of the first mip level into memory memcpy(data, ktx_image_data, ktx_image_size); vkUnmapMemory(get_device().get_handle(), mappable_memory); // Linear tiled images don't need to be staged and can be directly used as textures texture.image = mappable_image; texture.device_memory = mappable_memory; texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Setup image memory barrier transfer image to shader read layout VkCommandBuffer copy_command = get_device().create_command_buffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // The sub resource range describes the regions of the image we will be transition VkImageSubresourceRange subresource_range = {}; subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresource_range.baseMipLevel = 0; subresource_range.levelCount = 1; subresource_range.layerCount = 1; // Transition the texture image layout to shader read, so it can be sampled from VkImageMemoryBarrier image_memory_barrier = vkb::initializers::image_memory_barrier(); ; image_memory_barrier.image = texture.image; image_memory_barrier.subresourceRange = subresource_range; image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Insert a memory dependency at the proper pipeline stages that will execute the image layout transition // Source pipeline stage is host write/read execution (VK_PIPELINE_STAGE_HOST_BIT) // Destination pipeline stage fragment shader access (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) vkCmdPipelineBarrier( copy_command, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); get_device().flush_command_buffer(copy_command, queue, true); } // now, the ktx_texture can be destroyed ktxTexture_Destroy(ktx_texture); // Calculate valid filter and mipmap modes VkFilter filter = VK_FILTER_LINEAR; VkSamplerMipmapMode mipmap_mode = VK_SAMPLER_MIPMAP_MODE_LINEAR; vkb::make_filters_valid(get_device().get_gpu().get_handle(), format, &filter, &mipmap_mode); // Create a texture sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the texture data. This means you could have multiple sampler objects for the same texture with different settings // Note: Similar to the samplers available with OpenGL 3.3 VkSamplerCreateInfo sampler = vkb::initializers::sampler_create_info(); sampler.magFilter = filter; sampler.minFilter = filter; sampler.mipmapMode = mipmap_mode; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; // Set max level-of-detail to mip level count of the texture sampler.maxLod = (use_staging) ? static_cast(texture.mip_levels) : 0.0f; // Enable anisotropic filtering // This feature is optional, so we must check if it's supported on the device if (get_device().get_gpu().get_features().samplerAnisotropy) { // Use max. level of anisotropy for this example sampler.maxAnisotropy = get_device().get_gpu().get_properties().limits.maxSamplerAnisotropy; sampler.anisotropyEnable = VK_TRUE; } else { // The device does not support anisotropic filtering sampler.maxAnisotropy = 1.0; sampler.anisotropyEnable = VK_FALSE; } sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK(vkCreateSampler(get_device().get_handle(), &sampler, nullptr, &texture.sampler)); // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = vkb::initializers::image_view_create_info(); view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = (use_staging) ? texture.mip_levels : 1; // The view will be based on the texture's image view.image = texture.image; VK_CHECK(vkCreateImageView(get_device().get_handle(), &view, nullptr, &texture.view)); } // Free all Vulkan resources used by a texture object void TextureLoading::destroy_texture(Texture texture) { vkDestroyImageView(get_device().get_handle(), texture.view, nullptr); vkDestroyImage(get_device().get_handle(), texture.image, nullptr); vkDestroySampler(get_device().get_handle(), texture.sampler, nullptr); vkFreeMemory(get_device().get_handle(), texture.device_memory, nullptr); } void TextureLoading::build_command_buffers() { VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; clear_values[0].color = default_clear_color; clear_values[1].depthStencil = { 0.0f, 0 }; VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); render_pass_begin_info.renderPass = render_pass; render_pass_begin_info.renderArea.offset.x = 0; render_pass_begin_info.renderArea.offset.y = 0; render_pass_begin_info.renderArea.extent.width = width; render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) { // Set target frame buffer render_pass_begin_info.framebuffer = framebuffers[i]; VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_bg, 0, 1, &descriptor_set_bg, 0, NULL); vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background); vkCmdDraw(draw_cmd_buffers[i], 6, 1, 0, 0); //vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, NULL); //vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); //VkDeviceSize offsets[1] = { 0 }; //vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer->get(), offsets); //vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); //vkCmdDrawIndexed(draw_cmd_buffers[i], index_count, 1, 0, 0, 0); draw_point_cloud(draw_cmd_buffers[i]); //draw_ui(draw_cmd_buffers[i]); vkCmdEndRenderPass(draw_cmd_buffers[i]); VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); } } void TextureLoading::draw() { std::unique_lock lock(mtx); std::unique_lock lock_point(mtx_point); ApiVulkanSample::prepare_frame(); // --- 新增:在命令缓冲区中绘制点云 --- // 在 build_command_buffers 中已经构建了命令缓冲区,但点数据是动态的。 // 因此,我们在这里重新记录命令缓冲区。 // 注意:更高效的做法是使用动态顶点缓冲区或间接绘制,但对于 Demo 来说,重新记录是可以接受的。 build_command_buffers(); // 重新构建所有命令缓冲区以包含最新的点云 // Command buffer to be submitted to the queue submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; // Submit to queue VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); ApiVulkanSample::submit_frame(); } void TextureLoading::generate_quad() { // Setup vertices for a single uv-mapped quad made from two triangles //std::vector vertices = //{ // {{1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, // {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, // {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, // {{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}} }; std::vector vertices = { {{0.3f, 0.3f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, {{-0.3f, 0.3f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, {{-0.3f, -0.3f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, {{0.3f, -0.3f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}} }; // Setup indices std::vector indices = { 0, 1, 2, 2, 3, 0 }; index_count = static_cast(indices.size()); auto vertex_buffer_size = vkb::to_u32(vertices.size() * sizeof(TextureLoadingVertexStructure)); auto index_buffer_size = vkb::to_u32(indices.size() * sizeof(uint32_t)); // Create buffers // For the sake of simplicity we won't stage the vertex data to the gpu memory // Vertex buffer vertex_buffer = std::make_unique(get_device(), vertex_buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); vertex_buffer->update(vertices.data(), vertex_buffer_size); index_buffer = std::make_unique(get_device(), index_buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); index_buffer->update(indices.data(), index_buffer_size); } void TextureLoading::setup_descriptor_pool() { // Example uses one ubo and one image sampler std::vector pool_sizes = { vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4), vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4) }; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), 4); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } void TextureLoading::setup_descriptor_set_layout_bg() { std::vector set_layout_bindings = { // Binding 1 : Fragment shader image sampler vkb::initializers::descriptor_set_layout_binding( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0) }; VkDescriptorSetLayoutCreateInfo descriptor_layout = vkb::initializers::descriptor_set_layout_create_info( set_layout_bindings.data(), static_cast(set_layout_bindings.size())); VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout, nullptr, &descriptor_set_layout_bg)); VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info( &descriptor_set_layout_bg, 1); VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout_bg)); } void TextureLoading::setup_descriptor_set_layout() { std::vector set_layout_bindings = { // Binding 0 : Vertex shader uniform buffer vkb::initializers::descriptor_set_layout_binding( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // Binding 1 : Fragment shader image sampler vkb::initializers::descriptor_set_layout_binding( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) }; VkDescriptorSetLayoutCreateInfo descriptor_layout = vkb::initializers::descriptor_set_layout_create_info( set_layout_bindings.data(), static_cast(set_layout_bindings.size())); VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout, nullptr, &descriptor_set_layout)); VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info( &descriptor_set_layout, 1); VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout)); } void TextureLoading::setup_descriptor_set() { VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info( descriptor_pool, &descriptor_set_layout, 1); VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer_vs); // Setup a descriptor image info for the current texture to be used as a combined image sampler VkDescriptorImageInfo image_descriptor; image_descriptor.imageView = texture.view; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) image_descriptor.sampler = texture.sampler; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) image_descriptor.imageLayout = texture.image_layout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) std::vector write_descriptor_sets = { // Binding 0 : Vertex shader uniform buffer vkb::initializers::write_descriptor_set( descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), // Binding 1 : Fragment shader texture sampler // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; vkb::initializers::write_descriptor_set( descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split) 1, // Shader binding point 1 &image_descriptor) // Pointer to the descriptor image for our texture }; vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); } void TextureLoading::setup_descriptor_set_bg() { VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info( descriptor_pool, &descriptor_set_layout_bg, 1); VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set_bg)); VkDescriptorImageInfo image_descriptor; image_descriptor.imageView = cam_text.view; image_descriptor.sampler = cam_text.sampler; image_descriptor.imageLayout = cam_text.image_layout; std::vector write_descriptor_sets = { vkb::initializers::write_descriptor_set( descriptor_set_bg, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &image_descriptor) }; vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); } void TextureLoading::prepare_pipeline_bg() { VkPipelineInputAssemblyStateCreateInfo input_assembly_state = vkb::initializers::pipeline_input_assembly_state_create_info( VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterization_state = vkb::initializers::pipeline_rasterization_state_create_info( VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineColorBlendAttachmentState blend_attachment_state = vkb::initializers::pipeline_color_blend_attachment_state( 0xf, VK_FALSE); VkPipelineColorBlendStateCreateInfo color_blend_state = vkb::initializers::pipeline_color_blend_state_create_info( 1, &blend_attachment_state); VkPipelineDepthStencilStateCreateInfo depth_stencil_state = vkb::initializers::pipeline_depth_stencil_state_create_info( VK_FALSE, VK_FALSE, VK_COMPARE_OP_GREATER); VkPipelineViewportStateCreateInfo viewport_state = vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); VkPipelineMultisampleStateCreateInfo multisample_state = vkb::initializers::pipeline_multisample_state_create_info( VK_SAMPLE_COUNT_1_BIT, 0); std::vector dynamic_state_enables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = vkb::initializers::pipeline_dynamic_state_create_info( dynamic_state_enables.data(), static_cast(dynamic_state_enables.size()), 0); std::array shader_stages; shader_stages[0] = load_shader("texture_loading", "bg.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shader_stages[1] = load_shader("texture_loading", "bg.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); VkPipelineVertexInputStateCreateInfo vertex_input_state_bg = vkb::initializers::pipeline_vertex_input_state_create_info(); VkGraphicsPipelineCreateInfo pipeline_create_info = vkb::initializers::pipeline_create_info( pipeline_layout_bg, render_pass, 0); pipeline_create_info.pVertexInputState = &vertex_input_state_bg; pipeline_create_info.pInputAssemblyState = &input_assembly_state; pipeline_create_info.pRasterizationState = &rasterization_state; pipeline_create_info.pColorBlendState = &color_blend_state; pipeline_create_info.pMultisampleState = &multisample_state; pipeline_create_info.pViewportState = &viewport_state; pipeline_create_info.pDepthStencilState = &depth_stencil_state; pipeline_create_info.pDynamicState = &dynamic_state; pipeline_create_info.stageCount = static_cast(shader_stages.size()); pipeline_create_info.pStages = shader_stages.data(); VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipelines.background)); } void TextureLoading::prepare_pipelines() { VkPipelineInputAssemblyStateCreateInfo input_assembly_state = vkb::initializers::pipeline_input_assembly_state_create_info( VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterization_state = vkb::initializers::pipeline_rasterization_state_create_info( VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineColorBlendAttachmentState blend_attachment_state = vkb::initializers::pipeline_color_blend_attachment_state( 0xf, VK_FALSE); VkPipelineColorBlendStateCreateInfo color_blend_state = vkb::initializers::pipeline_color_blend_state_create_info( 1, &blend_attachment_state); // Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept VkPipelineDepthStencilStateCreateInfo depth_stencil_state = vkb::initializers::pipeline_depth_stencil_state_create_info( VK_TRUE, VK_TRUE, VK_COMPARE_OP_GREATER); VkPipelineViewportStateCreateInfo viewport_state = vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); VkPipelineMultisampleStateCreateInfo multisample_state = vkb::initializers::pipeline_multisample_state_create_info( VK_SAMPLE_COUNT_1_BIT, 0); std::vector dynamic_state_enables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = vkb::initializers::pipeline_dynamic_state_create_info( dynamic_state_enables.data(), static_cast(dynamic_state_enables.size()), 0); // Load shaders std::array shader_stages; shader_stages[0] = load_shader("texture_loading", "texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shader_stages[1] = load_shader("texture_loading", "texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); // Vertex bindings and attributes const std::vector vertex_input_bindings = { vkb::initializers::vertex_input_binding_description(0, sizeof(TextureLoadingVertexStructure), VK_VERTEX_INPUT_RATE_VERTEX), }; const std::vector vertex_input_attributes = { vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(TextureLoadingVertexStructure, pos)), vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(TextureLoadingVertexStructure, uv)), vkb::initializers::vertex_input_attribute_description(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(TextureLoadingVertexStructure, normal)), }; VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info(); vertex_input_state.vertexBindingDescriptionCount = static_cast(vertex_input_bindings.size()); vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data(); vertex_input_state.vertexAttributeDescriptionCount = static_cast(vertex_input_attributes.size()); vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data(); VkGraphicsPipelineCreateInfo pipeline_create_info = vkb::initializers::pipeline_create_info( pipeline_layout, render_pass, 0); pipeline_create_info.pVertexInputState = &vertex_input_state; pipeline_create_info.pInputAssemblyState = &input_assembly_state; pipeline_create_info.pRasterizationState = &rasterization_state; pipeline_create_info.pColorBlendState = &color_blend_state; pipeline_create_info.pMultisampleState = &multisample_state; pipeline_create_info.pViewportState = &viewport_state; pipeline_create_info.pDepthStencilState = &depth_stencil_state; pipeline_create_info.pDynamicState = &dynamic_state; pipeline_create_info.stageCount = static_cast(shader_stages.size()); pipeline_create_info.pStages = shader_stages.data(); VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipelines.solid)); } // Prepare and initialize uniform buffer containing shader uniforms void TextureLoading::prepare_uniform_buffers() { // Vertex shader uniform buffer block uniform_buffer_vs = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); update_uniform_buffers(); } void TextureLoading::update_uniform_buffers() { // Vertex shader ubo_vs.projection = glm::perspective(glm::radians(60.0f), static_cast(width) / static_cast(height), 0.001f, 256.0f); glm::mat4 view_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, zoom)); ubo_vs.model = view_matrix * glm::translate(glm::mat4(1.0f), camera_pos); ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); uniform_buffer_vs->convert_and_update(ubo_vs); } bool TextureLoading::prepare(const vkb::ApplicationOptions& options) { if (!ApiVulkanSample::prepare(options)) { return false; } // --- 加载前景纹理 (示例) --- int width = 640; int height = 480; int rowStride = width*4; auto testImage = generateSimpleTestImage(width, height, 80); size_t dataSize = testImage.size(); std::cout << "Generated test image: " << width << "x" << height << std::endl; std::cout << "Row stride: " << rowStride << std::endl; std::cout << "Data size: " << dataSize << " bytes" << std::endl; processWithVulkan(testImage.data(), width, height, rowStride, dataSize, cam_text); load_texture(); generate_quad(); prepare_uniform_buffers(); setup_descriptor_set_layout(); setup_descriptor_set_layout_bg(); prepare_pipelines(); prepare_pipeline_bg(); setup_descriptor_pool(); setup_descriptor_set(); setup_descriptor_set_bg(); setup_point_descriptor_set_layout(); prepare_point_pipeline(); setup_point_descriptor_set(); float z = 0.1; //build_command_buffers(); float simPoint[] = { 1.0, 1.0, z, -1.0, 1.0, z, -1.0, -1.0, z, 1.0, -1.0, z, 0.8, 0.8, z, -0.8, 0.8, z, -0.8, -0.8, z, 0.8, -0.8, z, }; update_point_vertex_buffer(simPoint, 8); prepared = true; //start(); return true; } //void TextureLoading::updateTexture() //{ // std::unique_lock lock(mtx); // std::cout << "Working in thread: " << std::this_thread::get_id() << std::endl; // int width = 640; // int height = 480; // int rowStride; // auto testImage = generateSimpleTestImage(width, height, &rowStride); // size_t dataSize = testImage.size(); // processWithVulkan(testImage.data(), width, height, rowStride, dataSize, cam_text); //} // //void TextureLoading::run() { // std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // while (running) // { // updateTexture(); // std::this_thread::sleep_for(std::chrono::milliseconds(100)); // } //} // //void TextureLoading::start() { // running = true; // // 启动线程执行 run 方法 // workerThread = std::thread(&TextureLoading::run, this); //} // //void TextureLoading::stop() { // running = false; // if (workerThread.joinable()) { // workerThread.join(); // } //} void TextureLoading::render(float delta_time) { if (!prepared) { return; } draw(); } void TextureLoading::view_changed() { update_uniform_buffers(); } void TextureLoading::on_update_ui_overlay(vkb::Drawer& drawer) { if (drawer.header("Settings")) { if (drawer.slider_float("LOD bias", &ubo_vs.lod_bias, 0.0f, static_cast(texture.mip_levels))) { update_uniform_buffers(); } } } std::unique_ptr create_texture_loading() { return std::make_unique(); } TextureLoading* TextureLoading::this_instance = nullptr; void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize) { TextureLoading::Texture& cam_tex = TextureLoading::Get()->cam_text; TextureLoading::Get()->processWithVulkan(data, width, height, rowStride, dataSize, cam_tex); } void ReceiveFacePoint(float* pos, int pointCount, int width, int height) { //for (int i = 0; i < pointCount; i++) { // float x = pos[i * 3]; // float y = pos[i * 3 + 1]; // float z = pos[i * 3 + 2]; //} TextureLoading::Get()->update_point_vertex_buffer(pos, pointCount); } void TextureLoading::processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& out_texture) { std::unique_lock lock(mtx); VkDevice& device = get_device().get_handle(); const VkPhysicalDevice& physicalDevice = get_device().get_gpu().get_handle(); if (out_texture.image == VK_NULL_HANDLE) { createTexture(device, physicalDevice, width, height, out_texture); } const VkCommandPool& commandPool = get_device().get_command_pool().get_handle(); updateTexture(device, physicalDevice, commandPool, queue, data, width, height, rowStride, dataSize, out_texture); // 如果纹理被更新,并且管线已经准备好,可能需要重建命令缓冲区 // 这取决于你的应用逻辑。简单起见,在外部函数中处理。 } // --- 以下函数保持不变 --- void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, Texture& texture) { texture.width = width; texture.height = height; texture.mip_levels = 1; VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; // RGBA_8888 imageInfo.extent.width = width; imageInfo.extent.height = height; imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; if (vkCreateImage(device, &imageInfo, nullptr, &texture.image) != VK_SUCCESS) { throw std::runtime_error("Failed to create image!"); } VkMemoryRequirements memRequirements; vkGetImageMemoryRequirements(device, texture.image, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (vkAllocateMemory(device, &allocInfo, nullptr, &texture.device_memory) != VK_SUCCESS) { throw std::runtime_error("Failed to allocate image memory!"); } vkBindImageMemory(device, texture.image, texture.device_memory, 0); VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = texture.image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; if (vkCreateImageView(device, &viewInfo, nullptr, &texture.view) != VK_SUCCESS) { throw std::runtime_error("Failed to create texture image view!"); } VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1.0f; samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; samplerInfo.unnormalizedCoordinates = VK_FALSE; samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 0.0f; if (vkCreateSampler(device, &samplerInfo, nullptr, &texture.sampler) != VK_SUCCESS) { throw std::runtime_error("Failed to create texture sampler!"); } texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue queue, uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture) { VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = dataSize; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; if (vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer) != VK_SUCCESS) { throw std::runtime_error("Failed to create staging buffer!"); } VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); if (vkAllocateMemory(device, &allocInfo, nullptr, &stagingBufferMemory) != VK_SUCCESS) { throw std::runtime_error("Failed to allocate staging buffer memory!"); } vkBindBufferMemory(device, stagingBuffer, stagingBufferMemory, 0); void* mappedData; vkMapMemory(device, stagingBufferMemory, 0, dataSize, 0, &mappedData); if (rowStride == width * 4) { memcpy(mappedData, data, dataSize); } else { uint8_t* dst = static_cast(mappedData); const uint8_t* src = data; size_t dstRowStride = width * 4; for (int y = 0; y < height; y++) { memcpy(dst, src, dstRowStride); dst += dstRowStride; src += rowStride; } } vkUnmapMemory(device, stagingBufferMemory); VkCommandBuffer commandBuffer = beginSingleTimeCommands(device, commandPool); transitionImageLayout(commandBuffer, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkBufferImageCopy region = {}; region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = { 0, 0, 0 }; region.imageExtent = { static_cast(width), static_cast(height), 1 }; vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); transitionImageLayout(commandBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); endSingleTimeCommands(device, commandPool, queue, commandBuffer); vkDestroyBuffer(device, stagingBuffer, nullptr); vkFreeMemory(device, stagingBufferMemory, nullptr); texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } uint32_t TextureLoading::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties memProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { return i; } } throw std::runtime_error("Failed to find suitable memory type!"); } VkCommandBuffer TextureLoading::beginSingleTimeCommands(VkDevice device, VkCommandPool commandPool) { VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandPool = commandPool; allocInfo.commandBufferCount = 1; VkCommandBuffer commandBuffer; vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(commandBuffer, &beginInfo); return commandBuffer; } void TextureLoading::endSingleTimeCommands(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkCommandBuffer commandBuffer) { vkEndCommandBuffer(commandBuffer); VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); vkQueueWaitIdle(queue); vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); } void TextureLoading::transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = oldLayout; barrier.newLayout = newLayout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else { throw std::invalid_argument("Unsupported layout transition!"); } vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); } void TextureLoading::setup_point_descriptor_set_layout() { // --- 修改:为点云 UBO 创建描述符集布局绑定,指向 binding 0 --- VkDescriptorSetLayoutBinding ubo_layout_binding{}; ubo_layout_binding.binding = 0; // 与着色器中的 layout(binding = 0) 匹配 ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ubo_layout_binding.descriptorCount = 1; ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // 仅在顶点着色器中使用 ubo_layout_binding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo layout_info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; layout_info.bindingCount = 1; layout_info.pBindings = &ubo_layout_binding; // 指向我们的 UBO 绑定 VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &layout_info, nullptr, &point_descriptor_set_layout)); // --- 修改结束 --- } void TextureLoading::setup_point_descriptor_set() { // --- 修改:分配点云描述符集 --- VkDescriptorSetAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; alloc_info.descriptorPool = descriptor_pool; // 使用您已有的描述符池 alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &point_descriptor_set_layout; VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &point_descriptor_set)); // --- 修改结束 --- // --- 新增:更新点云描述符集,指向已有的 uniform_buffer_vs --- update_point_descriptor_set(); // 调用辅助函数进行更新 // --- 新增结束 --- } // --- 新增:更新点云描述符集以指向 uniform_buffer_vs 的辅助函数 --- void TextureLoading::update_point_descriptor_set() { if (!uniform_buffer_vs) { // 检查主 UBO 缓冲区是否存在 LOGW("Main uniform buffer (uniform_buffer_vs) not created yet, cannot update point descriptor set."); return; } VkDescriptorBufferInfo buffer_info{}; buffer_info.buffer = uniform_buffer_vs->get_handle(); // 关键:指定要绑定的 UBO 数据在缓冲区中的范围 // 我们绑定整个 ubo_vs,因为着色器只访问前两个 mat4,其余部分被忽略但不影响 buffer_info.offset = 0; buffer_info.range = sizeof(ubo_vs); // 绑定整个结构体 VkWriteDescriptorSet descriptor_write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor_write.dstSet = point_descriptor_set; // 目标描述符集 descriptor_write.dstBinding = 0; // 目标绑定 (binding = 0) descriptor_write.dstArrayElement = 0; // 数组元素索引 (非数组) descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptor_write.descriptorCount = 1; // 更新一个描述符 descriptor_write.pBufferInfo = &buffer_info; // 指向缓冲区信息 // pImageInfo 和 pTexelBufferView 对于 Uniform Buffer 不需要 vkUpdateDescriptorSets(get_device().get_handle(), 1, &descriptor_write, 0, nullptr); } // --- 新增结束 --- void TextureLoading::prepare_point_pipeline() { // 创建管线布局 VkPipelineLayoutCreateInfo pipeline_layout_create_info{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipeline_layout_create_info.setLayoutCount = 1; pipeline_layout_create_info.pSetLayouts = &point_descriptor_set_layout; // 没有 push constants pipeline_layout_create_info.pushConstantRangeCount = 0; pipeline_layout_create_info.pPushConstantRanges = nullptr; VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &point_pipeline_layout)); // 加载着色器 std::array shader_stages; shader_stages[0] = load_shader("texture_loading", "pointcloud.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shader_stages[1] = load_shader("texture_loading", "pointcloud.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); // 顶点输入绑定描述 (告诉 Vulkan 顶点数据的格式) VkVertexInputBindingDescription vertex_input_binding_description{}; vertex_input_binding_description.binding = 0; // 绑定点 vertex_input_binding_description.stride = sizeof(PointVertex); // 每个顶点的字节大小 vertex_input_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; // 顶点输入属性描述 (告诉 Vulkan 每个属性在顶点结构中的位置) std::array vertex_input_attributes = { VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(PointVertex, x)}, // 位置 VkVertexInputAttributeDescription{1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(PointVertex, r)} // 颜色 }; VkPipelineVertexInputStateCreateInfo vertex_input_state{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; vertex_input_state.vertexBindingDescriptionCount = 1; vertex_input_state.pVertexBindingDescriptions = &vertex_input_binding_description; vertex_input_state.vertexAttributeDescriptionCount = static_cast(vertex_input_attributes.size()); vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data(); // 输入装配 (绘制点列表) VkPipelineInputAssemblyStateCreateInfo input_assembly_state{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; // 关键:绘制点 input_assembly_state.primitiveRestartEnable = VK_FALSE; // 视口和裁剪 VkPipelineViewportStateCreateInfo viewport_state{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; viewport_state.viewportCount = 1; viewport_state.scissorCount = 1; // 光栅化 VkPipelineRasterizationStateCreateInfo rasterization_state{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; rasterization_state.cullMode = VK_CULL_MODE_NONE; // 通常不对点进行剔除 rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterization_state.lineWidth = 1.0f; // 可以通过 VkPhysicalDeviceFeatures::wideLines 扩展来支持更宽的线 // 多重采样 VkPipelineMultisampleStateCreateInfo multisample_state{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; // 深度和模板测试 (通常对点云启用深度测试) VkPipelineDepthStencilStateCreateInfo depth_stencil_state{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; depth_stencil_state.depthTestEnable = VK_FALSE; depth_stencil_state.depthWriteEnable = VK_FALSE; depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS; // 或 VK_COMPARE_OP_LESS depth_stencil_state.depthBoundsTestEnable = VK_FALSE; depth_stencil_state.stencilTestEnable = VK_FALSE; // 颜色混合 (点通常不需要混合) VkPipelineColorBlendAttachmentState blend_attachment_state{}; blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blend_attachment_state.blendEnable = VK_FALSE; VkPipelineColorBlendStateCreateInfo color_blend_state{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; color_blend_state.attachmentCount = 1; color_blend_state.pAttachments = &blend_attachment_state; // 动态状态 (视口和裁剪矩形将在命令缓冲区中设置) std::vector dynamic_state_enables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; dynamic_state.dynamicStateCount = static_cast(dynamic_state_enables.size()); dynamic_state.pDynamicStates = dynamic_state_enables.data(); // 创建图形管线 VkGraphicsPipelineCreateInfo pipeline_create_info{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipeline_create_info.stageCount = static_cast(shader_stages.size()); pipeline_create_info.pStages = shader_stages.data(); pipeline_create_info.pVertexInputState = &vertex_input_state; pipeline_create_info.pInputAssemblyState = &input_assembly_state; pipeline_create_info.pViewportState = &viewport_state; pipeline_create_info.pRasterizationState = &rasterization_state; pipeline_create_info.pMultisampleState = &multisample_state; pipeline_create_info.pDepthStencilState = &depth_stencil_state; pipeline_create_info.pColorBlendState = &color_blend_state; pipeline_create_info.pDynamicState = &dynamic_state; pipeline_create_info.layout = point_pipeline_layout; pipeline_create_info.renderPass = render_pass; // 使用主渲染通道 pipeline_create_info.subpass = 0; // 主子通道 VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &point_pipeline)); } void TextureLoading::update_point_vertex_buffer(float* pos, int pointCount) { std::lock_guard lock(mtx_point); if (pointCount <= 0) { point_count = 0; return; // 没有点数据,无需更新 } point_count = pointCount; // 1. 准备顶点数据 std::vector vertices(point_count); // 假设 pos 数组是 [x0,y0,z0,x1,y1,z1,...] // 为了可视化,这里简单地将坐标映射为颜色 (0-1范围) float min_x = std::numeric_limits::max(), max_x = std::numeric_limits::lowest(); float min_y = std::numeric_limits::max(), max_y = std::numeric_limits::lowest(); float min_z = std::numeric_limits::max(), max_z = std::numeric_limits::lowest(); for (int i = 0; i < point_count; ++i) { float x = pos[i * 3 + 0]; float y = pos[i * 3 + 1]; float z = pos[i * 3 + 2]; min_x = std::min(min_x, x); max_x = std::max(max_x, x); min_y = std::min(min_y, y); max_y = std::max(max_y, y); min_z = std::min(min_z, z); max_z = std::max(max_z, z); } float range_x = max_x - min_x; float range_y = max_y - min_y; float range_z = max_z - min_z; if (range_x == 0) range_x = 1.0f; // 防止除零 if (range_y == 0) range_y = 1.0f; if (range_z == 0) range_z = 1.0f; for (int i = 0; i < point_count; ++i) { vertices[i].x = pos[i * 3 + 0]; vertices[i].y = pos[i * 3 + 1]; vertices[i].z = pos[i * 3 + 2]; // 简单颜色映射 vertices[i].r = (vertices[i].x - min_x) / range_x; vertices[i].g = (vertices[i].y - min_y) / range_y; vertices[i].b = (vertices[i].z - min_z) / range_z; } // 2. 更新或创建顶点缓冲区 VkDeviceSize buffer_size = sizeof(PointVertex) * point_count; if (!point_vertex_buffer || point_vertex_buffer->get_size() < buffer_size) { // 如果缓冲区不存在或太小,则重新创建 point_vertex_buffer.reset(); point_vertex_buffer = std::make_unique(get_device(), buffer_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU // CPU 可写,GPU 可读 ); } // 3. 将数据复制到缓冲区 void* mapped_data = point_vertex_buffer->map(); if (mapped_data) { memcpy(mapped_data, vertices.data(), buffer_size); point_vertex_buffer->unmap(); } else { LOGE("Failed to map point vertex buffer for update."); } } void TextureLoading::draw_point_cloud(VkCommandBuffer command_buffer) { if (point_count == 0 || !point_vertex_buffer) { return; // 没有点或缓冲区未准备好 } vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, point_pipeline); vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, point_pipeline_layout, 0, 1, &point_descriptor_set, 0, nullptr); VkDeviceSize offsets[] = { 0 }; vkCmdBindVertexBuffers(command_buffer, 0, 1, point_vertex_buffer->get(), offsets); vkCmdDraw(command_buffer, point_count, 1, 0, 0); // 绘制 point_count 个顶点 }