Files
2026-03-31 13:42:22 -05:00

448 lines
18 KiB
C++

#include "vui_internal.h"
namespace vui {
uint32_t Renderer::Impl::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(physDevice, &memProps);
for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProps.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
return 0;
}
VkShaderModule Renderer::Impl::createShaderModule(const uint32_t *code, size_t sizeBytes) {
VkShaderModuleCreateInfo ci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
ci.codeSize = sizeBytes;
ci.pCode = code;
VkShaderModule mod;
vkCreateShaderModule(device, &ci, nullptr, &mod);
return mod;
}
void Renderer::Impl::initRenderPass() {
VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapchainFormat;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorRef;
VkSubpassDependency dep{};
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
dep.dstSubpass = 0;
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dep.srcAccessMask = 0;
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo rpci{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
rpci.attachmentCount = 1;
rpci.pAttachments = &colorAttachment;
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
rpci.dependencyCount = 1;
rpci.pDependencies = &dep;
vkCreateRenderPass(device, &rpci, nullptr, &renderPass);
}
void Renderer::Impl::initPipeline() {
VkDescriptorSetLayoutBinding binding{};
binding.binding = 0;
binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo dslci{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
dslci.bindingCount = 1;
dslci.pBindings = &binding;
vkCreateDescriptorSetLayout(device, &dslci, nullptr, &descriptorSetLayout);
VkPushConstantRange pushRange{};
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pushRange.offset = 0;
pushRange.size = sizeof(float) * 16;
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
plci.setLayoutCount = 1;
plci.pSetLayouts = &descriptorSetLayout;
plci.pushConstantRangeCount = 1;
plci.pPushConstantRanges = &pushRange;
vkCreatePipelineLayout(device, &plci, nullptr, &pipelineLayout);
VkShaderModule vertMod = createShaderModule(vertShaderSpv, sizeof(vertShaderSpv));
VkShaderModule fragMod = createShaderModule(fragShaderSpv, sizeof(fragShaderSpv));
VkPipelineShaderStageCreateInfo stages[2]{};
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
stages[0].module = vertMod;
stages[0].pName = "main";
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stages[1].module = fragMod;
stages[1].pName = "main";
VkVertexInputBindingDescription bindingDesc{};
bindingDesc.binding = 0;
bindingDesc.stride = sizeof(Vertex);
bindingDesc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attrs[3]{};
attrs[0].location = 0;
attrs[0].binding = 0;
attrs[0].format = VK_FORMAT_R32G32_SFLOAT;
attrs[0].offset = offsetof(Vertex, pos);
attrs[1].location = 1;
attrs[1].binding = 0;
attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
attrs[1].offset = offsetof(Vertex, uv);
attrs[2].location = 2;
attrs[2].binding = 0;
attrs[2].format = VK_FORMAT_R8G8B8A8_UNORM;
attrs[2].offset = offsetof(Vertex, color);
VkPipelineVertexInputStateCreateInfo vertexInput{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
vertexInput.vertexBindingDescriptionCount = 1;
vertexInput.pVertexBindingDescriptions = &bindingDesc;
vertexInput.vertexAttributeDescriptionCount = 3;
vertexInput.pVertexAttributeDescriptions = attrs;
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo vps{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
vps.viewportCount = 1;
vps.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
raster.polygonMode = VK_POLYGON_MODE_FILL;
raster.cullMode = VK_CULL_MODE_NONE;
raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
raster.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState blendAttachment{};
blendAttachment.blendEnable = VK_TRUE;
blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
blendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
blendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
blend.attachmentCount = 1;
blend.pAttachments = &blendAttachment;
VkDynamicState dynStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dyn{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
dyn.dynamicStateCount = 2;
dyn.pDynamicStates = dynStates;
VkPipelineDepthStencilStateCreateInfo depthStencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
VkGraphicsPipelineCreateInfo pci{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
pci.stageCount = 2;
pci.pStages = stages;
pci.pVertexInputState = &vertexInput;
pci.pInputAssemblyState = &ia;
pci.pViewportState = &vps;
pci.pRasterizationState = &raster;
pci.pMultisampleState = &ms;
pci.pColorBlendState = &blend;
pci.pDynamicState = &dyn;
pci.pDepthStencilState = &depthStencil;
pci.layout = pipelineLayout;
pci.renderPass = renderPass;
pci.subpass = 0;
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pci, nullptr, &pipeline);
vkDestroyShaderModule(device, vertMod, nullptr);
vkDestroyShaderModule(device, fragMod, nullptr);
}
void Renderer::Impl::initSampler() {
VkSamplerCreateInfo sci{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
sci.magFilter = VK_FILTER_LINEAR;
sci.minFilter = VK_FILTER_LINEAR;
sci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sci.maxAnisotropy = 1.0f;
sci.maxLod = 1.0f;
vkCreateSampler(device, &sci, nullptr, &sampler);
}
void Renderer::Impl::initDescriptorPool() {
VkDescriptorPoolSize poolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256};
VkDescriptorPoolCreateInfo dpci{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
dpci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
dpci.maxSets = 256;
dpci.poolSizeCount = 1;
dpci.pPoolSizes = &poolSize;
vkCreateDescriptorPool(device, &dpci, nullptr, &descriptorPool);
}
void Renderer::Impl::initVertexBuffer(uint32_t count) {
if (vertexBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, vertexBuffer, nullptr);
vkFreeMemory(device, vertexMemory, nullptr);
}
vertexBufferCapacity = count;
VkBufferCreateInfo bci{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
bci.size = sizeof(Vertex) * count;
bci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(device, &bci, nullptr, &vertexBuffer);
VkMemoryRequirements memReq;
vkGetBufferMemoryRequirements(device, vertexBuffer, &memReq);
VkMemoryAllocateInfo mai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
mai.allocationSize = memReq.size;
mai.memoryTypeIndex = findMemoryType(memReq.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(device, &mai, nullptr, &vertexMemory);
vkBindBufferMemory(device, vertexBuffer, vertexMemory, 0);
}
void Renderer::Impl::uploadTextureData(TextureEntry &tex, int x, int y, int w, int h,
const void *rgba) {
VkDeviceSize imageSize = (VkDeviceSize)w * h * 4;
VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory;
VkBufferCreateInfo bci{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
bci.size = imageSize;
bci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
vkCreateBuffer(device, &bci, nullptr, &stagingBuffer);
VkMemoryRequirements memReq;
vkGetBufferMemoryRequirements(device, stagingBuffer, &memReq);
VkMemoryAllocateInfo mai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
mai.allocationSize = memReq.size;
mai.memoryTypeIndex = findMemoryType(memReq.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(device, &mai, nullptr, &stagingMemory);
vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0);
void *mapped;
vkMapMemory(device, stagingMemory, 0, imageSize, 0, &mapped);
memcpy(mapped, rgba, imageSize);
vkUnmapMemory(device, stagingMemory);
VkCommandPoolCreateInfo cpci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
cpci.queueFamilyIndex = queueFamily;
cpci.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
VkCommandPool tmpPool;
vkCreateCommandPool(device, &cpci, nullptr, &tmpPool);
VkCommandBufferAllocateInfo abci{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
abci.commandPool = tmpPool;
abci.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
abci.commandBufferCount = 1;
VkCommandBuffer cmd;
vkAllocateCommandBuffers(device, &abci, &cmd);
VkCommandBufferBeginInfo bbi{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
bbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmd, &bbi);
bool fullImage = (x == 0 && y == 0 && w == tex.width && h == tex.height);
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.image = tex.image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.layerCount = 1;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.oldLayout = fullImage ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cmd, 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.imageOffset = {x, y, 0};
region.imageExtent = {(uint32_t)w, (uint32_t)h, 1};
vkCmdCopyBufferToImage(cmd, stagingBuffer, tex.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(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
vkEndCommandBuffer(cmd);
VkSubmitInfo si{VK_STRUCTURE_TYPE_SUBMIT_INFO};
si.commandBufferCount = 1;
si.pCommandBuffers = &cmd;
vkQueueSubmit(graphicsQueue, 1, &si, VK_NULL_HANDLE);
vkQueueWaitIdle(graphicsQueue);
vkDestroyCommandPool(device, tmpPool, nullptr);
vkDestroyBuffer(device, stagingBuffer, nullptr);
vkFreeMemory(device, stagingMemory, nullptr);
}
int Renderer::Impl::allocTexture(int width, int height, const void *rgba) {
TextureEntry tex{};
tex.width = width;
tex.height = height;
tex.active = true;
VkImageCreateInfo ici{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
ici.imageType = VK_IMAGE_TYPE_2D;
ici.format = VK_FORMAT_R8G8B8A8_UNORM;
ici.extent = {(uint32_t)width, (uint32_t)height, 1};
ici.mipLevels = 1;
ici.arrayLayers = 1;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
vkCreateImage(device, &ici, nullptr, &tex.image);
VkMemoryRequirements memReq;
vkGetImageMemoryRequirements(device, tex.image, &memReq);
VkMemoryAllocateInfo mai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
mai.allocationSize = memReq.size;
mai.memoryTypeIndex = findMemoryType(memReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkAllocateMemory(device, &mai, nullptr, &tex.memory);
vkBindImageMemory(device, tex.image, tex.memory, 0);
VkImageViewCreateInfo vci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
vci.image = tex.image;
vci.viewType = VK_IMAGE_VIEW_TYPE_2D;
vci.format = VK_FORMAT_R8G8B8A8_UNORM;
vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vci.subresourceRange.levelCount = 1;
vci.subresourceRange.layerCount = 1;
vkCreateImageView(device, &vci, nullptr, &tex.view);
VkDescriptorSetAllocateInfo dsai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
dsai.descriptorPool = descriptorPool;
dsai.descriptorSetCount = 1;
dsai.pSetLayouts = &descriptorSetLayout;
vkAllocateDescriptorSets(device, &dsai, &tex.descriptorSet);
VkDescriptorImageInfo dii{};
dii.sampler = sampler;
dii.imageView = tex.view;
dii.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkWriteDescriptorSet wds{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
wds.dstSet = tex.descriptorSet;
wds.dstBinding = 0;
wds.descriptorCount = 1;
wds.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
wds.pImageInfo = &dii;
vkUpdateDescriptorSets(device, 1, &wds, 0, nullptr);
if (rgba)
uploadTextureData(tex, 0, 0, width, height, rgba);
int id = -1;
for (int i = 0; i < (int)textures.size(); i++) {
if (!textures[i].active) { id = i; break; }
}
if (id < 0) {
id = (int)textures.size();
textures.push_back(tex);
} else {
textures[id] = tex;
}
return id;
}
void Renderer::Impl::createWhiteTexture() {
uint32_t white = 0xFFFFFFFF;
allocTexture(1, 1, &white);
}
void Renderer::Impl::addQuad(int texId, float x0, float y0, float x1, float y1,
float u0, float v0, float u1, float v1, Color c) {
Vertex verts[6];
float positions[6][2] = {
{x0, y0}, {x1, y0}, {x1, y1},
{x0, y0}, {x1, y1}, {x0, y1}
};
float uvs[6][2] = {
{u0, v0}, {u1, v0}, {u1, v1},
{u0, v0}, {u1, v1}, {u0, v1}
};
for (int i = 0; i < 6; i++) {
float px = positions[i][0], py = positions[i][1];
currentTransform.transformPoint(px, py);
verts[i].pos[0] = px;
verts[i].pos[1] = py;
verts[i].uv[0] = uvs[i][0];
verts[i].uv[1] = uvs[i][1];
verts[i].color[0] = c.r;
verts[i].color[1] = c.g;
verts[i].color[2] = c.b;
verts[i].color[3] = c.a;
}
bool merged = false;
if (!drawCmds.empty()) {
auto &last = drawCmds.back();
bool sameScissor = (!last.hasScissor && scissorStack.empty()) ||
(last.hasScissor && !scissorStack.empty() &&
last.scissorX == scissorStack.back().x &&
last.scissorY == scissorStack.back().y &&
last.scissorW == scissorStack.back().w &&
last.scissorH == scissorStack.back().h);
if (last.textureId == texId && sameScissor) {
last.vertexCount += 6;
merged = true;
}
}
if (!merged) {
DrawCmd dc;
dc.textureId = texId;
dc.vertexOffset = (uint32_t)vertices.size();
dc.vertexCount = 6;
dc.hasScissor = !scissorStack.empty();
if (dc.hasScissor) {
dc.scissorX = scissorStack.back().x;
dc.scissorY = scissorStack.back().y;
dc.scissorW = scissorStack.back().w;
dc.scissorH = scissorStack.back().h;
}
drawCmds.push_back(dc);
}
for (int i = 0; i < 6; i++)
vertices.push_back(verts[i]);
}
} // namespace vui