diff --git a/samples/api/texture_loading/texture_loading.cpp b/samples/api/texture_loading/texture_loading.cpp index 804d26e..a982a8f 100644 --- a/samples/api/texture_loading/texture_loading.cpp +++ b/samples/api/texture_loading/texture_loading.cpp @@ -1,12 +1,31 @@ -#include "texture_loading.h" -TextureLoading* TextureLoading::loadTextIns = nullptr; +/* 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 = -2.5f; rotation = { 0.0f, 15.0f, 0.0f }; title = "Texture loading"; - loadTextIns = this; } TextureLoading::~TextureLoading() @@ -17,22 +36,16 @@ TextureLoading::~TextureLoading() // Note : Inherited destructor cleans up resources stored in base class vkDestroyPipeline(get_device().get_handle(), pipelines.solid, nullptr); - if (pipelines.background != VK_NULL_HANDLE) { - vkDestroyPipeline(get_device().get_handle(), pipelines.background, nullptr); - } + vkDestroyPipeline(get_device().get_handle(), pipelines.background, nullptr); vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr); - if (pipeline_layout_bg != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout_bg, nullptr); - } + vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout_bg, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr); - if (descriptor_set_layout_bg != VK_NULL_HANDLE) { - vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout_bg, nullptr); - } } destroy_texture(texture); destroy_texture(cam_text); + vertex_buffer.reset(); index_buffer.reset(); uniform_buffer_vs.reset(); @@ -48,26 +61,403 @@ void TextureLoading::request_gpu_features(vkb::PhysicalDevice& gpu) } } + +// 生成简单的测试图像数据(红绿蓝三色条) +std::vector generateSimpleTestImage(int width, int height, int* outRowStride = nullptr) +{ + int rowStride = width * 4; // RGBA 每个像素4字节 + if (outRowStride) + { + *outRowStride = rowStride; + } + + size_t dataSize = rowStride * height; + std::vector imageData(dataSize, 0); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int pixelOffset = y * rowStride + x * 4; + + // 简单分成三个区域:红、绿、蓝 + if (x < width / 3) + { + // 红色区域 + imageData[pixelOffset] = 255; // R + imageData[pixelOffset + 1] = 0; // G + imageData[pixelOffset + 2] = 0; // B + } + else if (x < 2 * width / 3) + { + // 绿色区域 + imageData[pixelOffset] = 0; // R + imageData[pixelOffset + 1] = 255; // G + imageData[pixelOffset + 2] = 0; // B + } + else + { + // 蓝色区域 + imageData[pixelOffset] = 0; // R + imageData[pixelOffset + 1] = 0; // G + imageData[pixelOffset + 2] = 255; // B + } + + imageData[pixelOffset + 3] = 255; // A (完全不透明) + } + } + + 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) { - if (texture.view != VK_NULL_HANDLE) vkDestroyImageView(get_device().get_handle(), texture.view, nullptr); - if (texture.image != VK_NULL_HANDLE) vkDestroyImage(get_device().get_handle(), texture.image, nullptr); - if (texture.sampler != VK_NULL_HANDLE) vkDestroySampler(get_device().get_handle(), texture.sampler, nullptr); - if (texture.device_memory != VK_NULL_HANDLE) vkFreeMemory(get_device().get_handle(), texture.device_memory, nullptr); - // Reset struct - texture = { 0 }; + 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(); - // 注意:深度缓冲区清除值改为 1.0f (最远) VkClearValue clear_values[2]; clear_values[0].color = default_clear_color; - clear_values[1].depthStencil = { 1.0f, 0 }; // 使用 1.0f 作为深度清除值 - //clear_values[1].depthStencil = { 0, 0 }; // 使用 1.0f 作为深度清除值 + 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; @@ -75,7 +465,7 @@ void TextureLoading::build_command_buffers() 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.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) @@ -93,30 +483,19 @@ void TextureLoading::build_command_buffers() VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); - // --- 绘制全屏背景 --- - if (cam_text.image != VK_NULL_HANDLE && cam_text.view != VK_NULL_HANDLE && cam_text.sampler != VK_NULL_HANDLE) { - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background); - //vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_bg, 0, 1, &descriptor_set_bg, 0, NULL); - // 绘制一个三角形 strip,覆盖整个屏幕 (NDC -1 to 1) - // 顶点顺序: (-1,-1), (1,-1), (-1,1), (1,1) - vkCmdDraw(draw_cmd_buffers[i], 4, 1, 0, 0); // 4 个顶点, 1 个实例, 从索引 0 开始 - } + vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background); + vkCmdDraw(draw_cmd_buffers[i], 6, 1, 0, 0); - // --- 绘制前景四边形 --- - // 确保前景管线和描述符集有效 - if (pipelines.solid != VK_NULL_HANDLE && descriptor_set != VK_NULL_HANDLE && - vertex_buffer && index_buffer) { - 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); + 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); + 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); - } + vkCmdDrawIndexed(draw_cmd_buffers[i], index_count, 1, 0, 0, 0); - draw_ui(draw_cmd_buffers[i]); // UI 通常在最后绘制 + draw_ui(draw_cmd_buffers[i]); vkCmdEndRenderPass(draw_cmd_buffers[i]); @@ -124,48 +503,6 @@ void TextureLoading::build_command_buffers() } } - -// 生成简单的测试图像数据(红绿蓝三色条) -std::vector generateSimpleTestImage(int width, int height, int* outRowStride = nullptr) { - int rowStride = width * 4; // RGBA 每个像素4字节 - if (outRowStride) { - *outRowStride = rowStride; - } - - size_t dataSize = rowStride * height; - std::vector imageData(dataSize, 0); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int pixelOffset = y * rowStride + x * 4; - - // 简单分成三个区域:红、绿、蓝 - if (x < width / 3) { - // 红色区域 - imageData[pixelOffset] = 255; // R - imageData[pixelOffset + 1] = 0; // G - imageData[pixelOffset + 2] = 0; // B - } - else if (x < 2 * width / 3) { - // 绿色区域 - imageData[pixelOffset] = 0; // R - imageData[pixelOffset + 1] = 255; // G - imageData[pixelOffset + 2] = 0; // B - } - else { - // 蓝色区域 - imageData[pixelOffset] = 0; // R - imageData[pixelOffset + 1] = 0; // G - imageData[pixelOffset + 2] = 255; // B - } - - imageData[pixelOffset + 3] = 255; // A (完全不透明) - } - } - - return imageData; -} - void TextureLoading::draw() { ApiVulkanSample::prepare_frame(); @@ -216,26 +553,30 @@ void TextureLoading::generate_quad() void TextureLoading::setup_descriptor_pool() { - // Example uses one ubo and two image samplers (one for foreground, one for background) - // 增加 Combined Image Sampler 的数量到 2 + // Example uses one ubo and one image sampler std::vector pool_sizes = { vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) // 改为 2 - }; + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), - 2); // 最大描述符集数量改为 2 + 2); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } +void TextureLoading::setup_descriptor_set_layout_bg() +{ + VkPipelineLayoutCreateInfo layout_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + vkCreatePipelineLayout(get_device().get_handle(), &layout_info, nullptr, &pipeline_layout_bg); +} + void TextureLoading::setup_descriptor_set_layout() { - // Foreground descriptor set layout (UBO + Texture) std::vector set_layout_bindings = { // Binding 0 : Vertex shader uniform buffer @@ -243,7 +584,7 @@ void TextureLoading::setup_descriptor_set_layout() VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), - // Binding 1 : Fragment shader image sampler (foreground texture) + // Binding 1 : Fragment shader image sampler vkb::initializers::descriptor_set_layout_binding( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, @@ -256,7 +597,6 @@ void TextureLoading::setup_descriptor_set_layout() VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout, nullptr, &descriptor_set_layout)); - // Foreground pipeline layout VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info( &descriptor_set_layout, @@ -265,38 +605,8 @@ void TextureLoading::setup_descriptor_set_layout() VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout)); } -// 新增:设置背景描述符集布局 (仅包含纹理) -void TextureLoading::setup_descriptor_set_layout_bg() -{ - std::vector set_layout_bindings_bg = - { - // Binding 0 : Fragment shader image sampler (background texture) - vkb::initializers::descriptor_set_layout_binding( - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - VK_SHADER_STAGE_FRAGMENT_BIT, - 0) // Binding 0 for background - }; - - VkDescriptorSetLayoutCreateInfo descriptor_layout_bg = - vkb::initializers::descriptor_set_layout_create_info( - set_layout_bindings_bg.data(), - static_cast(set_layout_bindings_bg.size())); - - VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout_bg, nullptr, &descriptor_set_layout_bg)); - - // Background pipeline layout - VkPipelineLayoutCreateInfo pipeline_layout_create_info_bg = - vkb::initializers::pipeline_layout_create_info( - &descriptor_set_layout_bg, - 1); - - VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info_bg, nullptr, &pipeline_layout_bg)); -} - - void TextureLoading::setup_descriptor_set() { - // Foreground descriptor set VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info( descriptor_pool, @@ -307,11 +617,11 @@ void TextureLoading::setup_descriptor_set() VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer_vs); - // Setup a descriptor image info for the current foreground texture + // 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; - image_descriptor.sampler = texture.sampler; - image_descriptor.imageLayout = texture.image_layout; + 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 = { @@ -321,53 +631,20 @@ void TextureLoading::setup_descriptor_set() VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), - // Binding 1 : Fragment shader texture sampler (foreground) + // 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, - 1, - &image_descriptor) + 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() +void TextureLoading::prepare_pipeline_bg() { - // Background descriptor set - VkDescriptorSetAllocateInfo alloc_info_bg = - vkb::initializers::descriptor_set_allocate_info( - descriptor_pool, - &descriptor_set_layout_bg, - 1); - - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info_bg, &descriptor_set_bg)); - - // Setup a descriptor image info for the background texture (cam_text) - VkDescriptorImageInfo image_descriptor_bg; - // 注意:这里使用 cam_text - image_descriptor_bg.imageView = cam_text.view; - image_descriptor_bg.sampler = cam_text.sampler; - image_descriptor_bg.imageLayout = cam_text.image_layout; // 应该是 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - - std::vector write_descriptor_sets_bg = - { - // Binding 0 : Fragment shader texture sampler (background) - vkb::initializers::write_descriptor_set( - descriptor_set_bg, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 0, // Binding 0 for background - &image_descriptor_bg) - }; - - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets_bg.size()), write_descriptor_sets_bg.data(), 0, NULL); -} - - -void TextureLoading::prepare_pipelines() -{ - // --- 前景管线 --- VkPipelineInputAssemblyStateCreateInfo input_assembly_state = vkb::initializers::pipeline_input_assembly_state_create_info( VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, @@ -377,7 +654,7 @@ void TextureLoading::prepare_pipelines() VkPipelineRasterizationStateCreateInfo rasterization_state = vkb::initializers::pipeline_rasterization_state_create_info( VK_POLYGON_MODE_FILL, - VK_CULL_MODE_NONE, // 保持不变,或根据需要调整 + VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); @@ -391,14 +668,11 @@ void TextureLoading::prepare_pipelines() 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, // depthTestEnable - VK_TRUE, // depthWriteEnable - VK_COMPARE_OP_ALWAYS); // VK_COMPARE_OP_LESS); - //VK_COMPARE_OP_GREATER); // Reversed depth buffer + VK_FALSE, + VK_FALSE, + VK_COMPARE_OP_GREATER); VkPipelineViewportStateCreateInfo viewport_state = vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); @@ -418,13 +692,89 @@ void TextureLoading::prepare_pipelines() static_cast(dynamic_state_enables.size()), 0); - // Load shaders (确保你有对应的 SPIR-V 文件) std::array shader_stages; - // 假设着色器文件名为 texture.vert.spv 和 texture.frag.spv + 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 (用于前景 quad) + // Vertex bindings and attributes const std::vector vertex_input_bindings = { vkb::initializers::vertex_input_binding_description(0, sizeof(TextureLoadingVertexStructure), VK_VERTEX_INPUT_RATE_VERTEX), }; @@ -441,7 +791,7 @@ void TextureLoading::prepare_pipelines() VkGraphicsPipelineCreateInfo pipeline_create_info = vkb::initializers::pipeline_create_info( - pipeline_layout, // 前景管线布局 + pipeline_layout, render_pass, 0); @@ -459,93 +809,6 @@ void TextureLoading::prepare_pipelines() VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipelines.solid)); } -// 新增:准备背景管线 -void TextureLoading::prepare_pipeline_bg() -{ - // --- 背景管线 (简单 quad, 全屏覆盖) --- - VkPipelineInputAssemblyStateCreateInfo input_assembly_state_bg = - vkb::initializers::pipeline_input_assembly_state_create_info( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, // 使用 Triangle Strip - 0, - VK_FALSE); - - VkPipelineRasterizationStateCreateInfo rasterization_state_bg = - vkb::initializers::pipeline_rasterization_state_create_info( - VK_POLYGON_MODE_FILL, - VK_CULL_MODE_BACK_BIT, // 通常对全屏 quad 启用背面剔除,但这里可能不需要,因为是屏幕对齐的 - VK_FRONT_FACE_COUNTER_CLOCKWISE, - 0); - - VkPipelineColorBlendAttachmentState blend_attachment_state_bg = - vkb::initializers::pipeline_color_blend_attachment_state( - 0xf, - VK_FALSE); // 通常背景不需要混合 - - VkPipelineColorBlendStateCreateInfo color_blend_state_bg = - vkb::initializers::pipeline_color_blend_state_create_info( - 1, - &blend_attachment_state_bg); - - // 背景管线配置:启用深度测试,但禁用深度写入,使用 LESS 比较 (或 ALWAYS) - // 这样背景会被绘制,但不会影响前景物体的深度值(如果前景物体 Z 值更近) - VkPipelineDepthStencilStateCreateInfo depth_stencil_state_bg = - vkb::initializers::pipeline_depth_stencil_state_create_info( - VK_TRUE, // depthTestEnable - 启用深度测试 - VK_FALSE, // depthWriteEnable - 禁用深度写入,让前景物体能正确遮挡背景 - VK_COMPARE_OP_ALWAYS); //VK_COMPARE_OP_LESS); // 比较操作:如果片段的深度小于深度缓冲区中的值,则通过。 - // 结合清除深度为 1.0 和前景使用 GREATER,这应该可以正确工作。 - // 或者使用 VK_COMPARE_OP_ALWAYS 来强制绘制背景,忽略深度。 - - VkPipelineViewportStateCreateInfo viewport_state_bg = - vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0); - - VkPipelineMultisampleStateCreateInfo multisample_state_bg = - vkb::initializers::pipeline_multisample_state_create_info( - VK_SAMPLE_COUNT_1_BIT, - 0); - - // 背景管线不需要动态状态或顶点输入(如果顶点在着色器中生成) - std::vector dynamic_state_enables_bg = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamic_state_bg = - vkb::initializers::pipeline_dynamic_state_create_info( - dynamic_state_enables_bg.data(), - static_cast(dynamic_state_enables_bg.size()), - 0); - - // Load shaders for background (你需要创建对应的 SPIR-V 文件) - // 假设着色器文件名为 bg.vert.spv 和 bg.frag.spv - std::array shader_stages_bg; - shader_stages_bg[0] = load_shader("texture_loading", "bg.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shader_stages_bg[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(); - // vertex_input_state_bg 保持默认空状态即可 - - VkGraphicsPipelineCreateInfo pipeline_create_info_bg = - vkb::initializers::pipeline_create_info( - pipeline_layout_bg, // 背景管线布局 - render_pass, - 0); - - pipeline_create_info_bg.pVertexInputState = &vertex_input_state_bg; // 空的顶点输入 - pipeline_create_info_bg.pInputAssemblyState = &input_assembly_state_bg; - pipeline_create_info_bg.pRasterizationState = &rasterization_state_bg; - pipeline_create_info_bg.pColorBlendState = &color_blend_state_bg; - pipeline_create_info_bg.pMultisampleState = &multisample_state_bg; - pipeline_create_info_bg.pViewportState = &viewport_state_bg; - pipeline_create_info_bg.pDepthStencilState = &depth_stencil_state_bg; - pipeline_create_info_bg.pDynamicState = &dynamic_state_bg; - pipeline_create_info_bg.stageCount = static_cast(shader_stages_bg.size()); - pipeline_create_info_bg.pStages = shader_stages_bg.data(); - - VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info_bg, nullptr, &pipelines.background)); -} - - // Prepare and initialize uniform buffer containing shader uniforms void TextureLoading::prepare_uniform_buffers() { @@ -581,38 +844,26 @@ bool TextureLoading::prepare(const vkb::ApplicationOptions& options) return false; } - // --- 初始化 cam_text 纹理对象 --- - // 注意:这里只是初始化结构体,图像内存等由外部函数分配 - // cam_text = {0}; // 已在构造函数或销毁时重置 - // --- 加载前景纹理 (示例) --- - int width = 640; - int height = 480; - int rowStride; - auto testImage = generateSimpleTestImage(width, height, &rowStride); + int width = 640; + int height = 480; + int rowStride; + auto testImage = generateSimpleTestImage(width, height, &rowStride); 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, 1, rowStride, dataSize, texture); + processWithVulkan(testImage.data(), width, height, rowStride, dataSize, cam_text); - auto bgImage = generateSimpleTestImage(width, height, &rowStride); - dataSize = bgImage.size(); - processWithVulkan(bgImage.data(), width, height, 1, rowStride, dataSize, cam_text); - - // --- 生成前景几何体 --- + load_texture(); generate_quad(); - - // --- 准备资源 --- prepare_uniform_buffers(); - setup_descriptor_set_layout(); // 前景 DSL - setup_descriptor_set_layout_bg(); // 背景 DSL - prepare_pipelines(); // 前景管线 - prepare_pipeline_bg(); // 背景管线 - setup_descriptor_pool(); // 描述符池 (已更新大小) - setup_descriptor_set(); // 前景 DS - setup_descriptor_set_bg(); // 背景 DS - + setup_descriptor_set_layout(); + setup_descriptor_set_layout_bg(); + prepare_pipelines(); + prepare_pipeline_bg(); + setup_descriptor_pool(); + setup_descriptor_set(); build_command_buffers(); prepared = true; return true; @@ -624,15 +875,6 @@ void TextureLoading::render(float delta_time) { return; } - - // 可以在这里添加逻辑来更新 cam_text 纹理 - // 例如,如果 cam_text.image != VK_NULL_HANDLE,调用 updateTexture - - if (texture.width == 0) // 检查前景纹理 - { - return; - } - draw(); } @@ -657,31 +899,14 @@ std::unique_ptr create_texture_loading() return std::make_unique(); } -// 外部函数保持不变 -void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int format, int rowStride, size_t dataSize) -{ - if (TextureLoading::Get()) { - TextureLoading::Texture& cam_texture = TextureLoading::Get()->cam_text; - TextureLoading::Get()->processWithVulkan(data, width, height, format, rowStride, dataSize, cam_texture); - // 可能需要重建命令缓冲区或更新描述符集,如果管线已经创建 - // 这里简单地重建命令缓冲区 - if (TextureLoading::Get()->prepared) { - TextureLoading::Get()->build_command_buffers(); - } - } -} - - -void TextureLoading::processWithVulkan(uint8_t* data, int width, int height, int format, int rowStride, size_t dataSize, Texture& out_texture) +void TextureLoading::processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& out_texture) { 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, format, out_texture); - // 如果纹理是新创建的,并且管线已经准备好,需要更新描述符集 - if (prepared && &out_texture == &cam_text) { - setup_descriptor_set_bg(); // 重新设置背景描述符集 - } + if (out_texture.image == VK_NULL_HANDLE) + { + createTexture(device, physicalDevice, width, height, out_texture); + } const VkCommandPool& commandPool = get_device().get_command_pool().get_handle(); @@ -692,9 +917,8 @@ void TextureLoading::processWithVulkan(uint8_t* data, int width, int height, int } // --- 以下函数保持不变 --- -void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDevice, - int width, int height, int format, Texture& texture) { - +void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, Texture& texture) +{ texture.width = width; texture.height = height; texture.mip_levels = 1; @@ -702,7 +926,7 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev 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.format = VK_FORMAT_R8G8B8A8_UNORM; // RGBA_8888 imageInfo.extent.width = width; imageInfo.extent.height = height; imageInfo.extent.depth = 1; @@ -714,7 +938,8 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - if (vkCreateImage(device, &imageInfo, nullptr, &texture.image) != VK_SUCCESS) { + if (vkCreateImage(device, &imageInfo, nullptr, &texture.image) != VK_SUCCESS) + { throw std::runtime_error("Failed to create image!"); } @@ -727,7 +952,8 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (vkAllocateMemory(device, &allocInfo, nullptr, &texture.device_memory) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &texture.device_memory) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate image memory!"); } @@ -744,7 +970,8 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - if (vkCreateImageView(device, &viewInfo, nullptr, &texture.view) != VK_SUCCESS) { + if (vkCreateImageView(device, &viewInfo, nullptr, &texture.view) != VK_SUCCESS) + { throw std::runtime_error("Failed to create texture image view!"); } @@ -766,7 +993,8 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 0.0f; - if (vkCreateSampler(device, &samplerInfo, nullptr, &texture.sampler) != VK_SUCCESS) { + if (vkCreateSampler(device, &samplerInfo, nullptr, &texture.sampler) != VK_SUCCESS) + { throw std::runtime_error("Failed to create texture sampler!"); } @@ -776,9 +1004,9 @@ void TextureLoading::createTexture(VkDevice device, VkPhysicalDevice physicalDev 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; + int rowStride, size_t dataSize, Texture& texture) +{ + VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; VkBufferCreateInfo bufferInfo = {}; @@ -787,7 +1015,8 @@ void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDev bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - if (vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer) != VK_SUCCESS) { + if (vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer) != VK_SUCCESS) + { throw std::runtime_error("Failed to create staging buffer!"); } @@ -798,10 +1027,10 @@ void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDev 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); + memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - if (vkAllocateMemory(device, &allocInfo, nullptr, &stagingBufferMemory) != VK_SUCCESS) { + if (vkAllocateMemory(device, &allocInfo, nullptr, &stagingBufferMemory) != VK_SUCCESS) + { throw std::runtime_error("Failed to allocate staging buffer memory!"); } @@ -810,15 +1039,18 @@ void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDev void* mappedData; vkMapMemory(device, stagingBufferMemory, 0, dataSize, 0, &mappedData); - if (rowStride == width * 4) { + if (rowStride == width * 4) + { memcpy(mappedData, data, dataSize); } - else { + else + { uint8_t* dst = static_cast(mappedData); const uint8_t* src = data; - size_t dstRowStride = width * 4; + size_t dstRowStride = width * 4; - for (int y = 0; y < height; y++) { + for (int y = 0; y < height; y++) + { memcpy(dst, src, dstRowStride); dst += dstRowStride; src += rowStride; @@ -843,7 +1075,7 @@ void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDev region.imageSubresource.layerCount = 1; region.imageOffset = { 0, 0, 0 }; region.imageExtent = { static_cast(width), - static_cast(height), 1 }; + static_cast(height), 1 }; vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); @@ -861,13 +1093,16 @@ void TextureLoading::updateTexture(VkDevice device, VkPhysicalDevice physicalDev } uint32_t TextureLoading::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, - VkMemoryPropertyFlags properties) { + VkMemoryPropertyFlags properties) +{ VkPhysicalDeviceMemoryProperties memProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) + { if ((typeFilter & (1 << i)) && - (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + { return i; } } @@ -875,7 +1110,8 @@ uint32_t TextureLoading::findMemoryType(VkPhysicalDevice physicalDevice, uint32_ throw std::runtime_error("Failed to find suitable memory type!"); } -VkCommandBuffer TextureLoading::beginSingleTimeCommands(VkDevice device, VkCommandPool commandPool) { +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; @@ -895,7 +1131,8 @@ VkCommandBuffer TextureLoading::beginSingleTimeCommands(VkDevice device, VkComma } void TextureLoading::endSingleTimeCommands(VkDevice device, VkCommandPool commandPool, - VkQueue queue, VkCommandBuffer commandBuffer) { + VkQueue queue, VkCommandBuffer commandBuffer) +{ vkEndCommandBuffer(commandBuffer); VkSubmitInfo submitInfo = {}; @@ -910,7 +1147,8 @@ void TextureLoading::endSingleTimeCommands(VkDevice device, VkCommandPool comman } void TextureLoading::transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, - VkImageLayout oldLayout, VkImageLayout newLayout) { + VkImageLayout oldLayout, VkImageLayout newLayout) +{ VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = oldLayout; @@ -928,7 +1166,8 @@ void TextureLoading::transitionImageLayout(VkCommandBuffer commandBuffer, VkImag VkPipelineStageFlags destinationStage; if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && - newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -936,17 +1175,19 @@ void TextureLoading::transitionImageLayout(VkCommandBuffer commandBuffer, VkImag destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && - newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_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 { + else + { throw std::invalid_argument("Unsupported layout transition!"); } vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); -} \ No newline at end of file +} diff --git a/samples/api/texture_loading/texture_loading.h b/samples/api/texture_loading/texture_loading.h index a98285a..f97e403 100644 --- a/samples/api/texture_loading/texture_loading.h +++ b/samples/api/texture_loading/texture_loading.h @@ -1,10 +1,31 @@ -#pragma once +/* Copyright (c) 2019-2024, 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) + */ + +#pragma once #include #include "api_vulkan_sample.h" -// 顶点结构保持不变,因为背景 quad 可能不需要顶点缓冲区 + // Vertex layout for this example struct TextureLoadingVertexStructure { float pos[3]; @@ -15,6 +36,8 @@ struct TextureLoadingVertexStructure class TextureLoading : public ApiVulkanSample { public: + // Contains all Vulkan objects that are required to store and use a texture + // Note that this repository contains a texture class (vulkan_texture.h) that encapsulates texture loading functionality in a class that is used in subsequent demos struct Texture { VkSampler sampler; @@ -27,7 +50,7 @@ public: }; Texture texture = { 0 }; - Texture cam_text = { 0 }; // 将由外部函数初始化 + Texture cam_text = { 0 }; std::unique_ptr vertex_buffer; std::unique_ptr index_buffer; @@ -45,56 +68,45 @@ public: struct { - VkPipeline solid; // 原有前景管线 - VkPipeline background; // 新增背景管线 + VkPipeline solid; + VkPipeline background; } pipelines; - // 管线布局 - VkPipelineLayout pipeline_layout; // 前景管线布局 - VkPipelineLayout pipeline_layout_bg; // 背景管线布局 - - // 描述符集 - VkDescriptorSet descriptor_set; // 前景描述符集 - VkDescriptorSet descriptor_set_bg; // 背景描述符集 - - // 描述符集布局 - VkDescriptorSetLayout descriptor_set_layout; // 前景描述符集布局 - VkDescriptorSetLayout descriptor_set_layout_bg; // 背景描述符集布局 (仅包含 combined image sampler) - - static TextureLoading* Get() { - return loadTextIns; - } - static TextureLoading* loadTextIns; + VkPipelineLayout pipeline_layout; + VkPipelineLayout pipeline_layout_bg; + VkDescriptorSet descriptor_set; + VkDescriptorSetLayout descriptor_set_layout; TextureLoading(); ~TextureLoading(); virtual void request_gpu_features(vkb::PhysicalDevice& gpu) override; + void load_texture(); + void destroy_texture(Texture texture); void build_command_buffers() override; void draw(); - void generate_quad(); // 生成前景 quad + void generate_quad(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); - void setup_descriptor_set_layout_bg(); // 新增:设置背景描述符集布局 + void setup_descriptor_set_layout_bg(); void setup_descriptor_set(); - void setup_descriptor_set_bg(); // 新增:设置背景描述符集 void prepare_pipelines(); - void prepare_pipeline_bg(); // 新增:准备背景管线 void prepare_uniform_buffers(); void update_uniform_buffers(); bool prepare(const vkb::ApplicationOptions& options) override; virtual void render(float delta_time) override; virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer& drawer) override; - void destroy_texture(Texture texture); - // 现有函数保持不变 - void processWithVulkan(uint8_t* data, int width, int height, int format, int rowStride, size_t dataSize, Texture& out_texture); - void createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, int format, Texture& texture); - void updateTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue queue, uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture); - uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties); + void processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& out_texture); + void createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, Texture& texture); + void updateTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue queue, uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture); + uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties); VkCommandBuffer beginSingleTimeCommands(VkDevice device, VkCommandPool commandPool); - void endSingleTimeCommands(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkCommandBuffer commandBuffer); - void transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout); + void endSingleTimeCommands(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkCommandBuffer commandBuffer); + void transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout); + +public: + void prepare_pipeline_bg(); }; -std::unique_ptr create_texture_loading(); \ No newline at end of file +std::unique_ptr create_texture_loading();