png支持
This commit is contained in:
+279
-158
@@ -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, ®ion);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user