png支持

This commit is contained in:
ArchZer0
2026-08-17 10:26:27 +08:00
parent 07aeced2e4
commit 63d97f058a
25 changed files with 893 additions and 261 deletions
+279 -158
View File
@@ -9,7 +9,10 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <png.h>
#include <stdexcept>
#include <unordered_map>
#include <vector>
namespace boundard::render {
@@ -133,6 +136,41 @@ VkShaderModule create_shader_module(VkDevice device, const uint32_t* code, size_
return module;
}
std::vector<uint32_t> load_shader(const char* name) {
const std::string path = std::string(BOUNDARD_SHADER_DIR) + "/" + name;
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
throw std::runtime_error("failed to open shader: " + path);
}
const std::streamsize size = file.tellg();
if (size <= 0 || size % static_cast<std::streamsize>(sizeof(uint32_t)) != 0) {
throw std::runtime_error("invalid SPIR-V shader: " + path);
}
std::vector<uint32_t> code(static_cast<size_t>(size) / sizeof(uint32_t));
file.seekg(0);
file.read(reinterpret_cast<char*>(code.data()), size);
if (!file) {
throw std::runtime_error("failed to read shader: " + path);
}
return code;
}
bool load_png_rgba(const std::string& path, uint32_t& width, uint32_t& height,
std::vector<unsigned char>& pixels) {
png_image image{};
image.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&image, path.c_str())) {
return false;
}
image.format = PNG_FORMAT_RGBA;
width = image.width;
height = image.height;
pixels.resize(PNG_IMAGE_SIZE(image));
const bool loaded = png_image_finish_read(&image, nullptr, pixels.data(), 0, nullptr) != 0;
png_image_free(&image);
return loaded;
}
} // namespace
struct Renderer::Impl {
@@ -157,7 +195,6 @@ struct Renderer::Impl {
VkRenderPass render_pass = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipeline pipeline = VK_NULL_HANDLE;
@@ -165,18 +202,19 @@ struct Renderer::Impl {
std::vector<FrameData> frames;
std::vector<VkFence> images_in_flight;
VkBuffer vertex_buffer = VK_NULL_HANDLE;
VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE;
VkBuffer index_buffer = VK_NULL_HANDLE;
VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE;
uint32_t cached_index_count = 0;
uint32_t cached_vertex_count = 0;
const Mesh* cached_mesh = nullptr;
bool mesh_uploaded = false;
VkBuffer uniform_buffer = VK_NULL_HANDLE;
VkDeviceMemory uniform_buffer_memory = VK_NULL_HANDLE;
VkDeviceSize uniform_buffer_size = sizeof(float) * 16;
void* uniform_mapped = nullptr;
struct MeshResource {
VkBuffer vertex_buffer = VK_NULL_HANDLE;
VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE;
VkBuffer index_buffer = VK_NULL_HANDLE;
VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE;
uint32_t index_count = 0;
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory image_memory = VK_NULL_HANDLE;
VkImageView image_view = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
};
std::unordered_map<const Mesh*, MeshResource> mesh_resources;
size_t current_frame = 0;
bool initialized = false;
@@ -185,6 +223,9 @@ struct Renderer::Impl {
void init(int width, int height, const char* title);
void draw(const Actor* const* actors, size_t actor_count);
void upload_mesh(const Mesh& mesh);
void create_texture(MeshResource& resource, const std::string& path);
VkCommandBuffer begin_transfer();
void end_transfer(VkCommandBuffer command_buffer);
void destroy();
};
@@ -856,9 +897,9 @@ void Renderer::Impl::init(int width, int height, const char* title) {
VkDescriptorSetLayoutBinding layout_binding{};
layout_binding.binding = 0;
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layout_binding.descriptorCount = 1;
layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo descriptor_layout_info{};
descriptor_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
@@ -871,7 +912,7 @@ void Renderer::Impl::init(int width, int height, const char* title) {
VkPushConstantRange push_constant_range{};
push_constant_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(float) * 4;
push_constant_range.size = sizeof(float) * 20;
VkPipelineLayoutCreateInfo pipeline_layout_info{};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
@@ -883,50 +924,23 @@ void Renderer::Impl::init(int width, int height, const char* title) {
"vkCreatePipelineLayout");
VkDescriptorPoolSize pool_size{};
pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
pool_size.descriptorCount = 1;
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptorCount = 1024;
VkDescriptorPoolCreateInfo pool_info{};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.maxSets = 1;
pool_info.maxSets = 1024;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
check(vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptor_pool),
"vkCreateDescriptorPool");
VkDescriptorSetAllocateInfo set_allocate_info{};
set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
set_allocate_info.descriptorPool = descriptor_pool;
set_allocate_info.descriptorSetCount = 1;
set_allocate_info.pSetLayouts = &descriptor_set_layout;
check(vkAllocateDescriptorSets(device, &set_allocate_info, &descriptor_set),
"vkAllocateDescriptorSets");
create_buffer(physical_device, device, uniform_buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
uniform_buffer, uniform_buffer_memory);
check(vkMapMemory(device, uniform_buffer_memory, 0, uniform_buffer_size, 0, &uniform_mapped),
"vkMapMemory");
VkDescriptorBufferInfo buffer_info{};
buffer_info.buffer = uniform_buffer;
buffer_info.offset = 0;
buffer_info.range = uniform_buffer_size;
VkWriteDescriptorSet descriptor_write{};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.dstSet = descriptor_set;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptor_write.descriptorCount = 1;
descriptor_write.pBufferInfo = &buffer_info;
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);
const std::vector<uint32_t> vertex_shader = load_shader("textured_quad.vert.spv");
const std::vector<uint32_t> fragment_shader = load_shader("textured_quad.frag.spv");
VkShaderModule vertex_module = create_shader_module(
device, kQuadVertShader.data(), kQuadVertShader.size() * sizeof(kQuadVertShader[0]));
device, vertex_shader.data(), vertex_shader.size() * sizeof(uint32_t));
VkShaderModule fragment_module = create_shader_module(
device, kQuadFragShader.data(), kQuadFragShader.size() * sizeof(kQuadFragShader[0]));
device, fragment_shader.data(), fragment_shader.size() * sizeof(uint32_t));
VkPipelineShaderStageCreateInfo vertex_stage{};
vertex_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -947,7 +961,7 @@ void Renderer::Impl::init(int width, int height, const char* title) {
vertex_binding.stride = sizeof(Mesh::Vertex);
vertex_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::array<VkVertexInputAttributeDescription, 2> vertex_attributes{};
std::array<VkVertexInputAttributeDescription, 3> vertex_attributes{};
vertex_attributes[0].binding = 0;
vertex_attributes[0].location = 0;
vertex_attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
@@ -956,6 +970,10 @@ void Renderer::Impl::init(int width, int height, const char* title) {
vertex_attributes[1].location = 1;
vertex_attributes[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
vertex_attributes[1].offset = offsetof(Mesh::Vertex, color);
vertex_attributes[2].binding = 0;
vertex_attributes[2].location = 2;
vertex_attributes[2].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attributes[2].offset = offsetof(Mesh::Vertex, uv);
VkPipelineVertexInputStateCreateInfo vertex_input{};
vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
@@ -1005,7 +1023,13 @@ void Renderer::Impl::init(int width, int height, const char* title) {
multisampling.sampleShadingEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState color_blend_attachment{};
color_blend_attachment.blendEnable = VK_FALSE;
color_blend_attachment.blendEnable = VK_TRUE;
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
@@ -1040,58 +1064,172 @@ void Renderer::Impl::init(int width, int height, const char* title) {
initialized = true;
}
VkCommandBuffer Renderer::Impl::begin_transfer() {
VkCommandBufferAllocateInfo allocate_info{};
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
check(vkAllocateCommandBuffers(device, &allocate_info, &command_buffer), "vkAllocateCommandBuffers");
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
check(vkBeginCommandBuffer(command_buffer, &begin_info), "vkBeginCommandBuffer");
return command_buffer;
}
void Renderer::Impl::end_transfer(VkCommandBuffer command_buffer) {
check(vkEndCommandBuffer(command_buffer), "vkEndCommandBuffer");
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
check(vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE), "vkQueueSubmit");
check(vkQueueWaitIdle(graphics_queue), "vkQueueWaitIdle");
vkFreeCommandBuffers(device, command_pool, 1, &command_buffer);
}
void Renderer::Impl::create_texture(MeshResource& resource, const std::string& path) {
uint32_t width = 1;
uint32_t height = 1;
std::vector<unsigned char> pixels = {255, 255, 255, 255};
if (!path.empty() && !load_png_rgba(path, width, height, pixels)) {
std::fprintf(stderr, "无法加载 PNG 纹理 %s,已使用白色纹理\n", path.c_str());
width = 1;
height = 1;
pixels = {255, 255, 255, 255};
}
VkBuffer staging_buffer = VK_NULL_HANDLE;
VkDeviceMemory staging_memory = VK_NULL_HANDLE;
const VkDeviceSize size = static_cast<VkDeviceSize>(pixels.size());
create_buffer(physical_device, device, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
staging_buffer, staging_memory);
void* mapped = nullptr;
check(vkMapMemory(device, staging_memory, 0, size, 0, &mapped), "vkMapMemory");
std::memcpy(mapped, pixels.data(), pixels.size());
vkUnmapMemory(device, staging_memory);
VkImageCreateInfo image_info{};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent = {width, height, 1};
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
check(vkCreateImage(device, &image_info, nullptr, &resource.image), "vkCreateImage");
VkMemoryRequirements requirements{};
vkGetImageMemoryRequirements(device, resource.image, &requirements);
VkMemoryAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = requirements.size;
alloc_info.memoryTypeIndex = find_memory_type(physical_device, requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
check(vkAllocateMemory(device, &alloc_info, nullptr, &resource.image_memory), "vkAllocateMemory");
check(vkBindImageMemory(device, resource.image, resource.image_memory, 0), "vkBindImageMemory");
VkCommandBuffer command_buffer = begin_transfer();
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = resource.image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.layerCount = 1;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
VkBufferImageCopy region{};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent = {width, height, 1};
vkCmdCopyBufferToImage(command_buffer, staging_buffer, resource.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
end_transfer(command_buffer);
vkDestroyBuffer(device, staging_buffer, nullptr);
vkFreeMemory(device, staging_memory, nullptr);
VkImageViewCreateInfo view_info{};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = resource.image;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = VK_FORMAT_R8G8B8A8_UNORM;
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.layerCount = 1;
check(vkCreateImageView(device, &view_info, nullptr, &resource.image_view), "vkCreateImageView");
VkSamplerCreateInfo sampler_info{};
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR;
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.maxLod = 0.0F;
check(vkCreateSampler(device, &sampler_info, nullptr, &resource.sampler), "vkCreateSampler");
VkDescriptorSetAllocateInfo set_info{};
set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
set_info.descriptorPool = descriptor_pool;
set_info.descriptorSetCount = 1;
set_info.pSetLayouts = &descriptor_set_layout;
check(vkAllocateDescriptorSets(device, &set_info, &resource.descriptor_set), "vkAllocateDescriptorSets");
VkDescriptorImageInfo image_descriptor{};
image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_descriptor.imageView = resource.image_view;
image_descriptor.sampler = resource.sampler;
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = resource.descriptor_set;
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &image_descriptor;
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
}
void Renderer::Impl::upload_mesh(const Mesh& mesh) {
if (cached_mesh == &mesh && mesh_uploaded) {
return;
}
if (mesh_resources.find(&mesh) != mesh_resources.end()) return;
MeshResource& resource = mesh_resources[&mesh];
resource.index_count = static_cast<uint32_t>(mesh.indices.size());
if (mesh.vertices.empty() || resource.index_count == 0) return;
if (vertex_buffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, vertex_buffer, nullptr);
vertex_buffer = VK_NULL_HANDLE;
}
if (vertex_buffer_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, vertex_buffer_memory, nullptr);
vertex_buffer_memory = VK_NULL_HANDLE;
}
if (index_buffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, index_buffer, nullptr);
index_buffer = VK_NULL_HANDLE;
}
if (index_buffer_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, index_buffer_memory, nullptr);
index_buffer_memory = VK_NULL_HANDLE;
}
cached_vertex_count = static_cast<uint32_t>(mesh.vertices.size());
cached_index_count = static_cast<uint32_t>(mesh.indices.size());
cached_mesh = &mesh;
mesh_uploaded = false;
if (cached_vertex_count == 0 || cached_index_count == 0) {
return;
}
const VkDeviceSize vertex_size = sizeof(Mesh::Vertex) * cached_vertex_count;
const VkDeviceSize vertex_size = sizeof(Mesh::Vertex) * mesh.vertices.size();
create_buffer(physical_device, device, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
vertex_buffer, vertex_buffer_memory);
resource.vertex_buffer, resource.vertex_buffer_memory);
void* vertex_mapped = nullptr;
check(vkMapMemory(device, vertex_buffer_memory, 0, vertex_size, 0, &vertex_mapped),
check(vkMapMemory(device, resource.vertex_buffer_memory, 0, vertex_size, 0, &vertex_mapped),
"vkMapMemory");
std::memcpy(vertex_mapped, mesh.vertices.data(), vertex_size);
vkUnmapMemory(device, vertex_buffer_memory);
vkUnmapMemory(device, resource.vertex_buffer_memory);
const VkDeviceSize index_size = sizeof(uint32_t) * cached_index_count;
const VkDeviceSize index_size = sizeof(uint32_t) * resource.index_count;
create_buffer(physical_device, device, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
index_buffer, index_buffer_memory);
resource.index_buffer, resource.index_buffer_memory);
void* index_mapped = nullptr;
check(vkMapMemory(device, index_buffer_memory, 0, index_size, 0, &index_mapped),
check(vkMapMemory(device, resource.index_buffer_memory, 0, index_size, 0, &index_mapped),
"vkMapMemory");
std::memcpy(index_mapped, mesh.indices.data(), index_size);
vkUnmapMemory(device, index_buffer_memory);
mesh_uploaded = true;
vkUnmapMemory(device, resource.index_buffer_memory);
create_texture(resource, mesh.texture);
}
void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
@@ -1120,7 +1258,6 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
images_in_flight[image_index] = frame.in_flight;
check(vkResetFences(device, 1, &frame.in_flight), "vkResetFences");
// 示例渲染器一次上传一个 Mesh;最后一个 Actor 的网格用于绑定。
for (size_t i = 0; i < actor_count; ++i) {
const Actor& actor = *actors[i];
const auto& mesh = actor.mesh();
@@ -1149,57 +1286,52 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
vkCmdBeginRenderPass(frame.command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
0, 1, &descriptor_set, 0, nullptr);
for (size_t i = 0; i < actor_count; ++i) {
const Actor& actor = *actors[i];
if (actor.mesh() == nullptr) continue;
const auto resource_it = mesh_resources.find(actor.mesh().get());
if (resource_it == mesh_resources.end()) continue;
const MeshResource& resource = resource_it->second;
if (resource.vertex_buffer == VK_NULL_HANDLE || resource.index_buffer == VK_NULL_HANDLE ||
resource.descriptor_set == VK_NULL_HANDLE || resource.index_count == 0) continue;
if (vertex_buffer == VK_NULL_HANDLE || index_buffer == VK_NULL_HANDLE ||
cached_index_count == 0 || cached_vertex_count == 0) {
vkCmdEndRenderPass(frame.command_buffer);
check(vkEndCommandBuffer(frame.command_buffer), "vkEndCommandBuffer");
} else {
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(frame.command_buffer, 0, 1, &vertex_buffer, &offset);
vkCmdBindIndexBuffer(frame.command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindVertexBuffers(frame.command_buffer, 0, 1, &resource.vertex_buffer, &offset);
vkCmdBindIndexBuffer(frame.command_buffer, resource.index_buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
0, 1, &resource.descriptor_set, 0, nullptr);
for (size_t i = 0; i < actor_count; ++i) {
const Actor& actor = *actors[i];
if (actor.mesh() == nullptr) {
continue;
}
const auto position = actor.position();
const auto facing = actor.facing();
const float scale = 0.35F;
float facing_x = facing[0];
float facing_y = facing[1];
const float facing_length = std::sqrt(facing_x * facing_x + facing_y * facing_y);
if (facing_length > 0.0001F) {
facing_x /= facing_length;
facing_y /= facing_length;
} else {
facing_x = 1.0F;
facing_y = 0.0F;
}
// 朝向向量作为局部 X 轴,旋转 + 平移,把单位四边形放到 Actor 的屏幕坐标。
std::array<float, 16> model = {{
scale * facing_x, scale * facing_y, 0.0F, 0.0F,
-scale * facing_y, scale * facing_x, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
position[0], position[1], 0.0F, 1.0F,
}};
std::memcpy(uniform_mapped, model.data(), uniform_buffer_size);
const std::array<float, 4> color = actor.mesh()->color();
vkCmdPushConstants(frame.command_buffer, pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(float) * 4, color.data());
vkCmdDrawIndexed(frame.command_buffer, cached_index_count, 1, 0, 0, 0);
const auto position = actor.position();
const auto facing = actor.facing();
const float scale = 0.35F;
float facing_x = facing[0];
float facing_y = facing[1];
const float facing_length = std::sqrt(facing_x * facing_x + facing_y * facing_y);
if (facing_length > 0.0001F) {
facing_x /= facing_length;
facing_y /= facing_length;
} else {
facing_x = 1.0F;
facing_y = 0.0F;
}
vkCmdEndRenderPass(frame.command_buffer);
check(vkEndCommandBuffer(frame.command_buffer), "vkEndCommandBuffer");
std::array<float, 20> constants = {{
scale * facing_x, scale * facing_y, 0.0F, 0.0F,
-scale * facing_y, scale * facing_x, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
position[0], position[1], 0.0F, 1.0F,
0.0F, 0.0F, 0.0F, 0.0F,
}};
const std::array<float, 4> color = actor.mesh()->color();
std::copy(color.begin(), color.end(), constants.begin() + 16);
vkCmdPushConstants(frame.command_buffer, pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(constants), constants.data());
vkCmdDrawIndexed(frame.command_buffer, resource.index_count, 1, 0, 0, 0);
}
vkCmdEndRenderPass(frame.command_buffer);
check(vkEndCommandBuffer(frame.command_buffer), "vkEndCommandBuffer");
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@@ -1208,6 +1340,7 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
submit_info.pWaitDstStageMask = &wait_stage;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &frame.command_buffer;
// Present 不提供 fence;同一交换链图像再次被获取前,不能重用其等待的信号量。
FrameData& image_frame = frames[image_index];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &image_frame.render_finished;
@@ -1238,11 +1371,6 @@ void Renderer::Impl::destroy() {
vkDeviceWaitIdle(device);
}
if (uniform_mapped != nullptr && uniform_buffer_memory != VK_NULL_HANDLE) {
vkUnmapMemory(device, uniform_buffer_memory);
uniform_mapped = nullptr;
}
for (FrameData& frame : frames) {
if (frame.in_flight != VK_NULL_HANDLE) {
vkDestroyFence(device, frame.in_flight, nullptr);
@@ -1258,24 +1386,17 @@ void Renderer::Impl::destroy() {
}
}
if (uniform_buffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, uniform_buffer, nullptr);
}
if (uniform_buffer_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, uniform_buffer_memory, nullptr);
}
if (vertex_buffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, vertex_buffer, nullptr);
}
if (vertex_buffer_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, vertex_buffer_memory, nullptr);
}
if (index_buffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, index_buffer, nullptr);
}
if (index_buffer_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, index_buffer_memory, nullptr);
for (auto& [mesh, resource] : mesh_resources) {
if (resource.sampler != VK_NULL_HANDLE) vkDestroySampler(device, resource.sampler, nullptr);
if (resource.image_view != VK_NULL_HANDLE) vkDestroyImageView(device, resource.image_view, nullptr);
if (resource.image != VK_NULL_HANDLE) vkDestroyImage(device, resource.image, nullptr);
if (resource.image_memory != VK_NULL_HANDLE) vkFreeMemory(device, resource.image_memory, nullptr);
if (resource.vertex_buffer != VK_NULL_HANDLE) vkDestroyBuffer(device, resource.vertex_buffer, nullptr);
if (resource.vertex_buffer_memory != VK_NULL_HANDLE) vkFreeMemory(device, resource.vertex_buffer_memory, nullptr);
if (resource.index_buffer != VK_NULL_HANDLE) vkDestroyBuffer(device, resource.index_buffer, nullptr);
if (resource.index_buffer_memory != VK_NULL_HANDLE) vkFreeMemory(device, resource.index_buffer_memory, nullptr);
}
mesh_resources.clear();
if (descriptor_pool != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(device, descriptor_pool, nullptr);