.
This commit is contained in:
1 parent
a015aa00ef
commit
a0b7e4c0d8
32 files changed
+1880
-88
No files matched your search
@@ -0,0 +1,828 @@
|
||||
//
|
||||
// Created by Vicente Ferrari Smith on 13.02.26.
|
||||
//
|
||||
|
||||
#include "../Grpahics.h"
|
||||
|
||||
#include <print>
|
||||
|
||||
#include "init.h"
|
||||
#include "../sprite.h"
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
#include <slang/slang.h>
|
||||
|
||||
extern int32_t window_width;
|
||||
extern int32_t window_height;
|
||||
|
||||
bool SortKey::operator<(const SortKey& b) const {
|
||||
if (depth != b.depth) return depth < b.depth;
|
||||
if (pipeline != b.pipeline) return pipeline < b.pipeline;
|
||||
return materialID < b.materialID;
|
||||
}
|
||||
|
||||
Renderer::Renderer(GLFWwindow *window) {
|
||||
|
||||
create_pipeline_layout();
|
||||
colored_quad_pipeline = create_graphics_pipeline<vertex_p2_s2_st2_col4_a1_u32>(
|
||||
device,
|
||||
pipelineLayout,
|
||||
swapchain_format.format,
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
true
|
||||
);
|
||||
create_default_sampler();
|
||||
create_descriptor_pool();
|
||||
createFrameResources();
|
||||
}
|
||||
|
||||
void Renderer::begin_frame() {
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
void Renderer::flush() {
|
||||
|
||||
}
|
||||
|
||||
void Renderer::submit_quad(glm::vec2 pos, glm::vec2 scale) {
|
||||
RenderCommand cmd {};
|
||||
cmd.pipeline = PipelineType::ColoredQuad;
|
||||
cmd.key = {
|
||||
(uint16_t) pos.y,
|
||||
0,
|
||||
(uint8_t) PipelineType::ColoredQuad
|
||||
};
|
||||
|
||||
cmd.colored_quad = {
|
||||
.pos = pos,
|
||||
.scale = scale,
|
||||
.color = {0, 1, 1, 1},
|
||||
};
|
||||
|
||||
commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void Renderer::submit_sprite(glm::vec2 pos, const sprite_t &sprite) {
|
||||
RenderCommand cmd {};
|
||||
cmd.pipeline = PipelineType::TexturedQuad;
|
||||
cmd.key = {
|
||||
(uint16_t) pos.y,
|
||||
0,
|
||||
(uint8_t) PipelineType::TexturedQuad
|
||||
};
|
||||
|
||||
cmd.textured_quad = {
|
||||
.position = pos,
|
||||
.size = {0, 0},
|
||||
.uvMin = {0, 0},
|
||||
.uvMax = {0, 0},
|
||||
.color = {1, 1, 1, 1},
|
||||
.textureID = 0,
|
||||
};
|
||||
|
||||
commands.push_back(cmd);
|
||||
|
||||
// assert(started == true, "You can't submit without having started the renderer first.");
|
||||
// renderable : Renderable;
|
||||
// renderable.type = .Sprite;
|
||||
//
|
||||
// if sprite.window_space
|
||||
// renderable.projection_type = .ORTHOGRAPHIC_WINDOW;
|
||||
// else
|
||||
// renderable.projection_type = .ORTHOGRAPHIC_WORLD;
|
||||
//
|
||||
// renderable.pos = pos;
|
||||
// renderable.sprite.texture_sheet = sprite.texture_sheet;
|
||||
// renderable.sprite.texture_cell = sprite.texture_cell;
|
||||
// renderable.sprite.origin = sprite.origin;
|
||||
// renderable.sprite.scale = sprite.scale;
|
||||
// renderable.sprite.colour = sprite.colour;
|
||||
// renderable.sprite.alpha = alpha;
|
||||
//
|
||||
// array_add(*renderer.renderable_list, renderable);
|
||||
}
|
||||
|
||||
void Renderer::create_pipeline_layout() {
|
||||
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {
|
||||
{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = nextTextureSlot,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VkDescriptorBindingFlags flags[1] = {
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutBindingFlagsCreateInfo layoutFlags{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindingFlags = flags
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo dslci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = &layoutFlags,
|
||||
// .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
||||
.bindingCount = bindings.size(),
|
||||
.pBindings = bindings.data()
|
||||
};
|
||||
|
||||
vkCreateDescriptorSetLayout(device, &dslci, nullptr, &descriptor_set_layout);
|
||||
|
||||
VkPushConstantRange push_constant{
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.offset = 0,
|
||||
.size = sizeof(glm::mat4),
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo plci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &descriptor_set_layout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &push_constant,
|
||||
};
|
||||
|
||||
vkCreatePipelineLayout(device, &plci, nullptr, &pipelineLayout);
|
||||
}
|
||||
|
||||
void Renderer::createFrameResources() {
|
||||
|
||||
const VkSemaphoreCreateInfo seci{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
};
|
||||
|
||||
VkFenceCreateInfo fenceInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
|
||||
VkCommandPoolCreateInfo cpci{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = queueFamily
|
||||
};
|
||||
|
||||
frames.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||
Frame &frame = frames[i];
|
||||
|
||||
vkCreateSemaphore(device, &seci, nullptr, &frame.imageAvailable);
|
||||
|
||||
vkCreateFence(device, &fenceInfo, nullptr, &frame.in_flight_fence);
|
||||
|
||||
|
||||
vkCreateCommandPool(device, &cpci, nullptr, &frame.commandPool);
|
||||
|
||||
const VkCommandBufferAllocateInfo cbai{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = frame.commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1
|
||||
};
|
||||
|
||||
vkAllocateCommandBuffers(device, &cbai, &frame.command_buffer);
|
||||
|
||||
VkBufferCreateInfo bufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = 1024 * 1024 * 4,
|
||||
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
};
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
||||
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
vmaCreateBuffer(
|
||||
allocator,
|
||||
&bufferInfo,
|
||||
&allocCreateInfo,
|
||||
&frame.vertexBuffer.buffer,
|
||||
&frame.vertexBuffer.allocation,
|
||||
&frame.vertexBuffer.info);
|
||||
}
|
||||
}
|
||||
|
||||
AllocatedBuffer Renderer::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) {
|
||||
// allocate buffer
|
||||
VkBufferCreateInfo bufferInfo = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
|
||||
bufferInfo.pNext = nullptr;
|
||||
bufferInfo.size = allocSize;
|
||||
|
||||
bufferInfo.usage = usage;
|
||||
|
||||
VmaAllocationCreateInfo vmaallocInfo = {};
|
||||
vmaallocInfo.usage = memoryUsage;
|
||||
vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
AllocatedBuffer newBuffer{};
|
||||
|
||||
// allocate the buffer
|
||||
vmaCreateBuffer(allocator, &bufferInfo, &vmaallocInfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info);
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
void Renderer::destroy_buffer(const AllocatedBuffer& buffer) {
|
||||
vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation);
|
||||
}
|
||||
|
||||
// GPUMeshBuffers Renderer::uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices) {
|
||||
// const size_t vertexBufferSize = vertices.size() * sizeof(vertex_p2_st2_col4_a1_u32);
|
||||
// const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
|
||||
//
|
||||
// GPUMeshBuffers newSurface;
|
||||
//
|
||||
// //create vertex buffer
|
||||
// newSurface.vertexBuffer = create_buffer(vertexBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
// VMA_MEMORY_USAGE_GPU_ONLY);
|
||||
//
|
||||
// //find the adress of the vertex buffer
|
||||
// VkBufferDeviceAddressInfo deviceAdressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,.buffer = newSurface.vertexBuffer.buffer };
|
||||
// newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(device, &deviceAdressInfo);
|
||||
//
|
||||
// //create index buffer
|
||||
// newSurface.indexBuffer = create_buffer(indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
// VMA_MEMORY_USAGE_GPU_ONLY);
|
||||
//
|
||||
// AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
|
||||
//
|
||||
// void* data = staging.allocation->GetMappedData();
|
||||
//
|
||||
// // copy vertex buffer
|
||||
// memcpy(data, vertices.data(), vertexBufferSize);
|
||||
// // copy index buffer
|
||||
// memcpy((char*)data + vertexBufferSize, indices.data(), indexBufferSize);
|
||||
//
|
||||
// immediate_submit([&](VkCommandBuffer cmd) {
|
||||
// VkBufferCopy vertexCopy{ 0 };
|
||||
// vertexCopy.dstOffset = 0;
|
||||
// vertexCopy.srcOffset = 0;
|
||||
// vertexCopy.size = vertexBufferSize;
|
||||
//
|
||||
// vkCmdCopyBuffer(cmd, staging.buffer, newSurface.vertexBuffer.buffer, 1, &vertexCopy);
|
||||
//
|
||||
// VkBufferCopy indexCopy{ 0 };
|
||||
// indexCopy.dstOffset = 0;
|
||||
// indexCopy.srcOffset = vertexBufferSize;
|
||||
// indexCopy.size = indexBufferSize;
|
||||
//
|
||||
// vkCmdCopyBuffer(cmd, staging.buffer, newSurface.indexBuffer.buffer, 1, &indexCopy);
|
||||
// });
|
||||
//
|
||||
// destroy_buffer(staging);
|
||||
//
|
||||
// return newSurface;
|
||||
//
|
||||
// }
|
||||
|
||||
VkPipeline Renderer::get_pipeline(PipelineType type) const {
|
||||
switch (type) {
|
||||
case PipelineType::TexturedQuad: return textured_quad_pipeline;
|
||||
case PipelineType::ColoredQuad: return colored_quad_pipeline;
|
||||
case PipelineType::Line: return line_pipeline;
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
||||
// void Renderer::bind_material(VkCommandBuffer cmd, uint16_t materialID) {
|
||||
// // In a real app, you'd have an array/map: std::vector<VkDescriptorSet> textureSets;
|
||||
// VkDescriptorSet set = textureSets[materialID];
|
||||
//
|
||||
// vkCmdBindDescriptorSets(
|
||||
// cmd,
|
||||
// VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
// pipelineLayout, // Our shared layout
|
||||
// 0, // Starting at Set 0
|
||||
// 1, // Binding 1 set
|
||||
// &set,
|
||||
// 0, nullptr
|
||||
// );
|
||||
// }
|
||||
|
||||
void Renderer::create_descriptor_pool() {
|
||||
VkDescriptorPoolSize pool_sizes[] = {
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nextTextureSlot },
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
||||
.maxSets = 1,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = pool_sizes
|
||||
};
|
||||
|
||||
vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = descriptorPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &descriptor_set_layout
|
||||
};
|
||||
|
||||
vkAllocateDescriptorSets(device, &alloc_info, &set);
|
||||
}
|
||||
|
||||
void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const {
|
||||
VkDescriptorImageInfo image_info{
|
||||
.sampler = sampler,
|
||||
.imageView = view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet write{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = set,
|
||||
.dstArrayElement = slot,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &image_info
|
||||
};
|
||||
|
||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||
}
|
||||
|
||||
void Renderer::upload_texture(
|
||||
const int w,
|
||||
const int h,
|
||||
const void* pixels,
|
||||
VkImage *image,
|
||||
VmaAllocation *allocation,
|
||||
VkImageView *view,
|
||||
uint32_t *descriptor_index)
|
||||
{
|
||||
VkDeviceSize imageSize = w * h * 4;
|
||||
|
||||
// --- 1. Create Staging Buffer (CPU Visible) ---
|
||||
VkBuffer stagingBuffer;
|
||||
VmaAllocation stagingAlloc;
|
||||
|
||||
VkBufferCreateInfo stagingBufferInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
stagingBufferInfo.size = imageSize;
|
||||
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
|
||||
VmaAllocationCreateInfo stagingAllocCreateInfo = {
|
||||
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||
};
|
||||
|
||||
VmaAllocationInfo stagingResultInfo;
|
||||
vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocCreateInfo, &stagingBuffer, &stagingAlloc, &stagingResultInfo);
|
||||
|
||||
// Copy raw pixels into the mapped memory provided by VMA
|
||||
memcpy(stagingResultInfo.pMappedData, pixels, imageSize);
|
||||
|
||||
// --- 2. Create GPU Image (Device Local / Tiled) ---
|
||||
VkExtent3D imageExtent = { (uint32_t) w, (uint32_t) h, 1 };
|
||||
|
||||
VkImageCreateInfo imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.extent = imageExtent,
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||||
};
|
||||
|
||||
VmaAllocationCreateInfo imageAllocCreateInfo = {
|
||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||
.priority = 1.0f,
|
||||
};
|
||||
|
||||
vmaCreateImage(allocator, &imageInfo, &imageAllocCreateInfo, image, allocation, nullptr);
|
||||
|
||||
// --- 3. The Transfer ---
|
||||
immediate_submit([&](VkCommandBuffer cmd) {
|
||||
// Transition image from UNDEFINED to TRANSFER_DST
|
||||
transition_image_layout(cmd, *image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkBufferImageCopy copyRegion = {};
|
||||
copyRegion.bufferOffset = 0;
|
||||
copyRegion.bufferRowLength = 0;
|
||||
copyRegion.bufferImageHeight = 0;
|
||||
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copyRegion.imageSubresource.mipLevel = 0;
|
||||
copyRegion.imageSubresource.baseArrayLayer = 0;
|
||||
copyRegion.imageSubresource.layerCount = 1;
|
||||
copyRegion.imageExtent = imageExtent;
|
||||
|
||||
vkCmdCopyBufferToImage(cmd, stagingBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region);
|
||||
|
||||
// Transition image from TRANSFER_DST to SHADER_READ_ONLY
|
||||
transition_image_layout(cmd, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
});
|
||||
|
||||
// Clean up temporary staging resources
|
||||
vmaDestroyBuffer(allocator, stagingBuffer, stagingAlloc);
|
||||
|
||||
// --- 4. Finalize Handles ---
|
||||
*view = create_image_view(*image, imageInfo.format);
|
||||
|
||||
// Register in your Bindless Array (Set 0, Binding 0, Index N)
|
||||
*descriptor_index = nextTextureSlot++;
|
||||
update_bindless_slot(*descriptor_index, *view, defaultSampler);
|
||||
}
|
||||
|
||||
void Renderer::immediate_submit(std::function<void(VkCommandBuffer)>&& func) const {
|
||||
VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
allocInfo.commandPool = frames[currentFrame].commandPool; // Use a pool created with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer cmd;
|
||||
vkAllocateCommandBuffers(device, &allocInfo, &cmd);
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
|
||||
vkBeginCommandBuffer(cmd, &beginInfo);
|
||||
|
||||
// Execute the code passed in the lambda
|
||||
func(cmd);
|
||||
|
||||
vkEndCommandBuffer(cmd);
|
||||
|
||||
VkSubmitInfo submit{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
submit.commandBufferCount = 1;
|
||||
submit.pCommandBuffers = &cmd;
|
||||
|
||||
// Submit and wait
|
||||
vkQueueSubmit(graphics_queue, 1, &submit, VK_NULL_HANDLE);
|
||||
vkQueueWaitIdle(graphics_queue);
|
||||
|
||||
vkFreeCommandBuffers(device, frames[currentFrame].commandPool, 1, &cmd);
|
||||
}
|
||||
|
||||
void Renderer::transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const {
|
||||
VkImageMemoryBarrier2 barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
|
||||
barrier.oldLayout = oldLayout;
|
||||
barrier.newLayout = newLayout;
|
||||
barrier.image = image;
|
||||
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
|
||||
// Simple synchronization: wait for all previous commands, and block all future ones
|
||||
// You can optimize these masks later, but this is safe for a 2D engine
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||
|
||||
VkDependencyInfo dep{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
dep.imageMemoryBarrierCount = 1;
|
||||
dep.pImageMemoryBarriers = &barrier;
|
||||
|
||||
vkCmdPipelineBarrier2(cmd, &dep);
|
||||
}
|
||||
|
||||
VkImageView Renderer::create_image_view(VkImage image, VkFormat format) const {
|
||||
VkImageViewCreateInfo viewInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
};
|
||||
|
||||
// Default component mapping (R,G,B,A)
|
||||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
|
||||
// Which part of the image to look at (Mip 0, Layer 0)
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
VkImageView view;
|
||||
vkCreateImageView(device, &viewInfo, nullptr, &view);
|
||||
return view;
|
||||
}
|
||||
|
||||
void Renderer::create_default_sampler() {
|
||||
VkSamplerCreateInfo samplerInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
// For crisp pixel art, use NEAREST. For smooth textures, use LINEAR.
|
||||
.magFilter = VK_FILTER_NEAREST,
|
||||
.minFilter = VK_FILTER_NEAREST,
|
||||
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||||
|
||||
// How to handle "out of bounds" UVs
|
||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
|
||||
// Optimization: turn off things we don't need for simple 2D
|
||||
.anisotropyEnable = VK_FALSE,
|
||||
.maxAnisotropy = 1.0f,
|
||||
.compareEnable = VK_FALSE,
|
||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
|
||||
vkCreateSampler(device, &samplerInfo, nullptr, &defaultSampler);
|
||||
}
|
||||
|
||||
void Renderer::end_frame() {
|
||||
|
||||
Frame &frame = frames[currentFrame];
|
||||
|
||||
vkWaitForFences(device, 1, &frame.in_flight_fence, VK_TRUE, UINT64_MAX);
|
||||
vkResetFences(device, 1, &frame.in_flight_fence);
|
||||
|
||||
uint32_t imageIndex;
|
||||
vkAcquireNextImageKHR(
|
||||
device,
|
||||
swapchain,
|
||||
UINT64_MAX,
|
||||
frame.imageAvailable,
|
||||
VK_NULL_HANDLE,
|
||||
&imageIndex
|
||||
);
|
||||
|
||||
commands = counting_sort_descending(commands, [](const RenderCommand &cmd){
|
||||
return cmd.key.depth;
|
||||
});
|
||||
|
||||
std::vector<vertex_p2_s2_st2_col4_a1_u32> vertices;
|
||||
|
||||
for (auto& cmd : commands) {
|
||||
|
||||
|
||||
switch (cmd.pipeline) {
|
||||
case PipelineType::ColoredQuad: {
|
||||
const auto &q = cmd.colored_quad;
|
||||
|
||||
// Calculate spatial corners
|
||||
//float x0 = q.position.x;
|
||||
//float y0 = q.position.y;
|
||||
//float x1 = q.position.x + q.size.x;
|
||||
//float y1 = q.position.y + q.size.y;
|
||||
|
||||
// Calculate UV corners
|
||||
// float u0 = q.uvMin.x;
|
||||
// float v0 = q.uvMin.y;
|
||||
// float u1 = q.uvMax.x;
|
||||
// float v1 = q.uvMax.y;
|
||||
|
||||
// Define the 4 corners of the quad
|
||||
vertex_p2_s2_st2_col4_a1_u32 vTL = { q.pos, q.scale, {0, 0}, {1, 0, 0, 0}, 1, 0 };
|
||||
vertex_p2_s2_st2_col4_a1_u32 vTR = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||
vertex_p2_s2_st2_col4_a1_u32 vBL = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||
vertex_p2_s2_st2_col4_a1_u32 vBR = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||
|
||||
// vertex_p2_st2_col4_a1_u32 vTL = { {x0, y0}, {u0, v0}, q.color, 1, q.textureID };
|
||||
// vertex_p2_st2_col4_a1_u32 vTR = { {x1, y0}, {u1, v0}, q.color, 1, q.textureID };
|
||||
// vertex_p2_st2_col4_a1_u32 vBL = { {x0, y1}, {u0, v1}, q.color, 1, q.textureID };
|
||||
// vertex_p2_st2_col4_a1_u32 vBR = { {x1, y1}, {u1, v1}, q.color, 1, q.textureID };
|
||||
|
||||
vertices.push_back(vTL);
|
||||
vertices.push_back(vBL);
|
||||
vertices.push_back(vTR);
|
||||
|
||||
vertices.push_back(vTR);
|
||||
vertices.push_back(vBL);
|
||||
vertices.push_back(vBR);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = frame.command_buffer;
|
||||
vkResetCommandBuffer(cmd, 0);
|
||||
|
||||
VkCommandBufferBeginInfo cbBI {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||
};
|
||||
vkBeginCommandBuffer(cmd, &cbBI);
|
||||
|
||||
recordCommandBuffer(
|
||||
cmd,
|
||||
images[imageIndex],
|
||||
imageViews[imageIndex],
|
||||
swapchain_extent,
|
||||
imageLayouts[imageIndex],
|
||||
frame,
|
||||
vertices
|
||||
);
|
||||
|
||||
vkEndCommandBuffer(cmd);
|
||||
|
||||
imageLayouts[imageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkSemaphoreSubmitInfo waitBinary{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.semaphore = frame.imageAvailable,
|
||||
.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
|
||||
};
|
||||
|
||||
VkSemaphoreSubmitInfo signalBinary{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.semaphore = renderFinished[imageIndex],
|
||||
.stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT
|
||||
};
|
||||
|
||||
VkCommandBufferSubmitInfo cmdInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||
.commandBuffer = cmd,
|
||||
};
|
||||
|
||||
const VkSubmitInfo2 submit{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
|
||||
.waitSemaphoreInfoCount = 1,
|
||||
.pWaitSemaphoreInfos = &waitBinary,
|
||||
.commandBufferInfoCount = 1,
|
||||
.pCommandBufferInfos = &cmdInfo,
|
||||
.signalSemaphoreInfoCount = 1,
|
||||
.pSignalSemaphoreInfos = &signalBinary,
|
||||
};
|
||||
|
||||
vkQueueSubmit2(graphics_queue, 1, &submit, frame.in_flight_fence);
|
||||
|
||||
VkPresentInfoKHR present{
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &renderFinished[imageIndex],
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = &swapchain,
|
||||
.pImageIndices = &imageIndex,
|
||||
};
|
||||
|
||||
vkQueuePresentKHR(graphics_queue, &present);
|
||||
|
||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
void Renderer::upload_vertex_buffer(
|
||||
VkCommandBuffer cmd,
|
||||
const Frame &frame,
|
||||
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const
|
||||
{
|
||||
VkMemoryPropertyFlags memPropFlags;
|
||||
vmaGetAllocationMemoryProperties(allocator, frame.vertexBuffer.allocation, &memPropFlags);
|
||||
|
||||
if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
|
||||
// The Allocation ended up in a mappable memory.
|
||||
// Calling vmaCopyMemoryToAllocation() does vmaMapMemory(), memcpy(), vmaUnmapMemory(), and vmaFlushAllocation().
|
||||
VkResult result = vmaCopyMemoryToAllocation(allocator, vertices.data(), frame.vertexBuffer.allocation, 0, vertices.size() * sizeof(vertex_p2_s2_st2_col4_a1_u32));
|
||||
// Check result...
|
||||
|
||||
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
|
||||
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
|
||||
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
bufMemBarrier.buffer = frame.vertexBuffer.buffer;
|
||||
bufMemBarrier.offset = 0;
|
||||
bufMemBarrier.size = VK_WHOLE_SIZE;
|
||||
|
||||
// It's important to insert a buffer memory barrier here to ensure writing to the buffer has finished.
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
|
||||
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::recordCommandBuffer(
|
||||
VkCommandBuffer cmd,
|
||||
VkImage image,
|
||||
VkImageView imageView,
|
||||
VkExtent2D extent,
|
||||
VkImageLayout oldLayout,
|
||||
const Frame &frame,
|
||||
const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const
|
||||
{
|
||||
|
||||
{
|
||||
VkImageMemoryBarrier2 toColor{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
toColor.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
toColor.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
toColor.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
toColor.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
toColor.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
toColor.image = image;
|
||||
toColor.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
|
||||
VkDependencyInfo dep{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &toColor
|
||||
};
|
||||
vkCmdPipelineBarrier2(cmd, &dep);
|
||||
}
|
||||
|
||||
VkClearValue clearColor = {{{0.1f, 0.1f, 0.2f, 1.0f}}};
|
||||
VkRenderingAttachmentInfo colorAttach{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = imageView,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.clearValue = clearColor
|
||||
};
|
||||
|
||||
VkRenderingInfo ri{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea = {{0,0}, extent},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &colorAttach
|
||||
};
|
||||
|
||||
upload_vertex_buffer(cmd, frame, vertices);
|
||||
|
||||
vkCmdBeginRendering(cmd, &ri);
|
||||
|
||||
VkViewport vp{0.0f, 0.0f, (float)extent.width, (float)extent.height, 0.0f, 1.0f};
|
||||
VkRect2D sc{{0, 0}, extent};
|
||||
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||
vkCmdSetScissor(cmd, 0, 1, &sc);
|
||||
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, 0, nullptr);
|
||||
VkDeviceSize vOffset{ 0 };
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &frame.vertexBuffer.buffer, &vOffset);
|
||||
|
||||
glm::mat4 projection = glm::ortho(0.0f, (float)window_width, 0.0f, (float)window_height, -1.0f, 1.0f);
|
||||
|
||||
vkCmdPushConstants(
|
||||
cmd,
|
||||
pipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0,
|
||||
sizeof(glm::mat4),
|
||||
&projection
|
||||
);
|
||||
|
||||
PipelineType lastPipeline = PipelineType::None; // Track current state
|
||||
// uint32_t vertexOffset = currentFrame * MAX_VERTICES_PER_BATCH;
|
||||
uint32_t currentBatchVertices = 0;
|
||||
|
||||
for (const auto & render_command : commands) {
|
||||
// Only switch pipelines if we have to
|
||||
if (render_command.pipeline != lastPipeline) {
|
||||
// If we were mid-batch, draw what we have before switching
|
||||
if (currentBatchVertices > 0) {
|
||||
vkCmdDraw(cmd, currentBatchVertices, 1, 0, 0);
|
||||
// vertexOffset += currentBatchVertices;
|
||||
currentBatchVertices = 0;
|
||||
}
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, get_pipeline(render_command.pipeline));
|
||||
lastPipeline = render_command.pipeline;
|
||||
}
|
||||
|
||||
currentBatchVertices += 6;
|
||||
}
|
||||
|
||||
// Draw the final batch
|
||||
if (currentBatchVertices > 0) {
|
||||
vkCmdDraw(cmd, currentBatchVertices, 1, 0, 0);
|
||||
}
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
|
||||
// 3. Transition back to Present
|
||||
{
|
||||
VkImageMemoryBarrier2 toPresent{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstAccessMask = 0,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.image = image,
|
||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||
};
|
||||
|
||||
VkDependencyInfo dep{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &toPresent
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier2(cmd, &dep);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
//
|
||||
// Created by Vicente Ferrari Smith on 13.02.26.
|
||||
//
|
||||
|
||||
#ifndef V_RENDERER_H
|
||||
#define V_RENDERER_H
|
||||
|
||||
#include "init.h"
|
||||
#include <volk/volk.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include "glm/gtx/string_cast.hpp"
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
#include "../sprite.h"
|
||||
#include "texture.h"
|
||||
#include <misc.h>
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <slang/slang.h>
|
||||
#include <slang/slang-com-ptr.h>
|
||||
|
||||
inline Slang::ComPtr<slang::IGlobalSession> slangGlobalSession;
|
||||
|
||||
enum class PROJECTION_TYPE : uint8_t {
|
||||
NONE,
|
||||
ORTHOGRAPHIC_WORLD,
|
||||
ORTHOGRAPHIC_WINDOW,
|
||||
PERSPECTIVE_WORLD,
|
||||
PERSPECTIVE_WINDOW,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
struct vertex_p2_s2_st2_col4_a1_u32 {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 scale;
|
||||
glm::vec2 uv;
|
||||
glm::vec4 color;
|
||||
float alpha;
|
||||
uint32_t textureID;
|
||||
|
||||
static VkVertexInputBindingDescription getBindingDescription() {
|
||||
return {0, sizeof(vertex_p2_s2_st2_col4_a1_u32), VK_VERTEX_INPUT_RATE_VERTEX};
|
||||
}
|
||||
|
||||
static std::array<VkVertexInputAttributeDescription, 6> getAttributeDescriptions() {
|
||||
return {
|
||||
{
|
||||
{0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, pos)},
|
||||
{1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, scale)},
|
||||
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, uv)},
|
||||
{3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, color)},
|
||||
{4, 0, VK_FORMAT_R32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, alpha)},
|
||||
{5, 0, VK_FORMAT_R32_UINT, offsetof(vertex_p2_s2_st2_col4_a1_u32, textureID)},
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// commands
|
||||
|
||||
enum class PipelineType : uint8_t {
|
||||
None,
|
||||
TexturedQuad,
|
||||
ColoredQuad,
|
||||
Line,
|
||||
Text,
|
||||
Chunk
|
||||
};
|
||||
|
||||
struct TexturedQuadCmd {
|
||||
glm::vec2 position;
|
||||
glm::vec2 size;
|
||||
glm::vec2 uvMin;
|
||||
glm::vec2 uvMax;
|
||||
glm::vec4 color;
|
||||
uint16_t textureID;
|
||||
};
|
||||
|
||||
struct ColoredQuadCmd {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 scale;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
struct LineCmd {
|
||||
glm::vec2 start;
|
||||
glm::vec2 end;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
// struct TextCmd {
|
||||
// Font* font;
|
||||
// std::string text;
|
||||
// glm::vec2 position;
|
||||
// glm::vec4 color;
|
||||
// };
|
||||
|
||||
struct ChunkCmd {
|
||||
VkBuffer vertexBuffer;
|
||||
VkBuffer indexBuffer;
|
||||
uint32_t indexCount;
|
||||
};
|
||||
|
||||
struct SortKey {
|
||||
uint16_t depth; // world Z or Y-sorted depth
|
||||
uint16_t materialID; // texture sheet, font atlas, etc.
|
||||
uint8_t pipeline; // PipelineType
|
||||
|
||||
bool operator<(const SortKey& b) const;
|
||||
};
|
||||
|
||||
struct RenderCommand {
|
||||
|
||||
SortKey key;
|
||||
PipelineType pipeline;
|
||||
|
||||
union {
|
||||
TexturedQuadCmd textured_quad;
|
||||
ColoredQuadCmd colored_quad;
|
||||
LineCmd line;
|
||||
// TextCmd text;
|
||||
ChunkCmd chunk;
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AllocatedBuffer {
|
||||
VkBuffer buffer;
|
||||
VmaAllocation allocation;
|
||||
VmaAllocationInfo info;
|
||||
};
|
||||
|
||||
struct GPUMeshBuffers {
|
||||
AllocatedBuffer indexBuffer;
|
||||
AllocatedBuffer vertexBuffer;
|
||||
VkDeviceAddress vertexBufferAddress;
|
||||
};
|
||||
|
||||
struct Renderer {
|
||||
std::vector<RenderCommand> commands{};
|
||||
|
||||
VkDescriptorSetLayout descriptor_set_layout{};
|
||||
VkPipelineLayout pipelineLayout{};
|
||||
VkPipeline textured_quad_pipeline{};
|
||||
VkPipeline colored_quad_pipeline{};
|
||||
VkPipeline line_pipeline{};
|
||||
VkPipeline text_pipeline{};
|
||||
VkPipeline chunk_pipeline{};
|
||||
VkDescriptorSet set{};
|
||||
|
||||
VkSampler defaultSampler{};
|
||||
|
||||
uint32_t nextTextureSlot = 0;
|
||||
|
||||
struct Frame {
|
||||
VkCommandPool commandPool{};
|
||||
VkCommandBuffer command_buffer{};
|
||||
|
||||
VkSemaphore imageAvailable{};
|
||||
VkFence in_flight_fence{};
|
||||
|
||||
AllocatedBuffer vertexBuffer{};
|
||||
};
|
||||
|
||||
std::vector<Frame> frames;
|
||||
uint32_t currentFrame = 0;
|
||||
|
||||
VkDescriptorPool descriptorPool{};
|
||||
std::vector<VkDescriptorSet> textureSets{};
|
||||
|
||||
void begin_frame();
|
||||
void end_frame();
|
||||
void flush();
|
||||
|
||||
void submit_sprite(glm::vec2 pos, const sprite_t &sprite);
|
||||
void submit_quad(glm::vec2 pos, glm::vec2 scale);
|
||||
|
||||
explicit Renderer(GLFWwindow *window);
|
||||
void create_pipeline_layout();
|
||||
void createFrameResources();
|
||||
void create_default_sampler();
|
||||
void recordCommandBuffer(
|
||||
VkCommandBuffer cmd,
|
||||
VkImage image,
|
||||
VkImageView imageView,
|
||||
VkExtent2D extent,
|
||||
VkImageLayout oldLayout,
|
||||
const Frame &frame,
|
||||
const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const;
|
||||
void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
|
||||
void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
|
||||
VkImageView create_image_view(VkImage image, VkFormat format) const;
|
||||
AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
|
||||
void destroy_buffer(const AllocatedBuffer& buffer);
|
||||
// GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
|
||||
void upload_vertex_buffer(
|
||||
VkCommandBuffer cmd,
|
||||
const Frame &frame,
|
||||
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
|
||||
|
||||
[[nodiscard]] VkPipeline get_pipeline(PipelineType type) const;
|
||||
// void bind_material(VkCommandBuffer cmd, uint16_t materialID);
|
||||
void create_descriptor_pool();
|
||||
void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
|
||||
|
||||
// Returns the resource info so the Manager can store it
|
||||
void upload_texture(
|
||||
int w,
|
||||
int h,
|
||||
const void* pixels,
|
||||
VkImage *image,
|
||||
VmaAllocation *allocation,
|
||||
VkImageView *view,
|
||||
uint32_t *descriptor_index);
|
||||
|
||||
template <typename T>
|
||||
VkPipeline create_graphics_pipeline(
|
||||
VkDevice device,
|
||||
VkPipelineLayout layout,
|
||||
VkFormat colorFormat,
|
||||
// VkShaderModule vertShader,
|
||||
// VkShaderModule fragShader,
|
||||
VkPrimitiveTopology topology,
|
||||
bool enableBlending)
|
||||
{
|
||||
|
||||
auto slangTargets{ std::to_array<slang::TargetDesc>({ {
|
||||
.format = SLANG_SPIRV,
|
||||
.profile = slangGlobalSession->findProfile("spirv_1_4")
|
||||
} })};
|
||||
auto slangOptions{ std::to_array<slang::CompilerOptionEntry>({ {
|
||||
slang::CompilerOptionName::EmitSpirvDirectly,
|
||||
{slang::CompilerOptionValueKind::Int, 1}
|
||||
} })};
|
||||
slang::SessionDesc slangSessionDesc{
|
||||
.targets = slangTargets.data(),
|
||||
.targetCount = SlangInt(slangTargets.size()),
|
||||
.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
|
||||
.compilerOptionEntries = slangOptions.data(),
|
||||
.compilerOptionEntryCount = uint32_t(slangOptions.size())
|
||||
};
|
||||
Slang::ComPtr<slang::ISession> slangSession;
|
||||
slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef());
|
||||
|
||||
Slang::ComPtr<slang::IModule> slangModule{
|
||||
slangSession->loadModuleFromSource("triangle", "shaders/shader.slang", nullptr, nullptr)
|
||||
};
|
||||
Slang::ComPtr<ISlangBlob> spirv;
|
||||
slangModule->getTargetCode(0, spirv.writeRef());
|
||||
|
||||
VkShaderModuleCreateInfo shaderModuleCI{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = spirv->getBufferSize(),
|
||||
.pCode = (uint32_t*)spirv->getBufferPointer()
|
||||
};
|
||||
VkShaderModule shaderModule{};
|
||||
vkCreateShaderModule(device, &shaderModuleCI, nullptr, &shaderModule);
|
||||
|
||||
auto vsCode = loadFile("shaders/triangle.vert.spv");
|
||||
auto fsCode = loadFile("shaders/triangle.frag.spv");
|
||||
|
||||
VkShaderModuleCreateInfo smci{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
|
||||
};
|
||||
|
||||
smci.codeSize = vsCode.size();
|
||||
smci.pCode = reinterpret_cast<uint32_t*>(vsCode.data());
|
||||
// VkShaderModule vs;
|
||||
// vkCreateShaderModule(device, &smci, nullptr, &vs);
|
||||
|
||||
smci.codeSize = fsCode.size();
|
||||
smci.pCode = reinterpret_cast<uint32_t*>(fsCode.data());
|
||||
// VkShaderModule fs;
|
||||
// vkCreateShaderModule(device, &smci, nullptr, &fs);
|
||||
|
||||
// --- Shaders ---
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages{
|
||||
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = shaderModule, .pName = "main"},
|
||||
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = shaderModule, .pName = "main" }
|
||||
};
|
||||
|
||||
// --- Vertex Input (Generic) ---
|
||||
auto binding = T::getBindingDescription();
|
||||
auto attrs = T::getAttributeDescriptions();
|
||||
|
||||
// --- Vertex Input (Matching our vertex_p2_st2_col4 struct) ---
|
||||
VkPipelineVertexInputStateCreateInfo vi{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = &binding,
|
||||
.vertexAttributeDescriptionCount = attrs.size(),
|
||||
.pVertexAttributeDescriptions = attrs.data(),
|
||||
};
|
||||
|
||||
// --- Input Assembly (Changes based on Topology parameter) ---
|
||||
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||
ia.topology = topology;
|
||||
|
||||
// --- Blending (Changes based on enableBlending parameter) ---
|
||||
VkPipelineColorBlendAttachmentState colorBlend{
|
||||
.blendEnable = enableBlending ? VK_TRUE : VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask = 0xF
|
||||
};
|
||||
|
||||
// --- Boilerplate (Standard 2D Defaults) ---
|
||||
VkPipelineViewportStateCreateInfo vp{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
1,
|
||||
nullptr,
|
||||
1,
|
||||
nullptr
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
VK_POLYGON_MODE_FILL,
|
||||
VK_CULL_MODE_NONE,
|
||||
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.0f
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SAMPLE_COUNT_1_BIT
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cb{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
VK_LOGIC_OP_AND,
|
||||
1,
|
||||
&colorBlend
|
||||
};
|
||||
|
||||
VkDynamicState dyns[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||
VkPipelineDynamicStateCreateInfo ds{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
2,
|
||||
dyns
|
||||
};
|
||||
|
||||
VkPipelineRenderingCreateInfo rci{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
1,
|
||||
&colorFormat
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo gpci{
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = &rci,
|
||||
.stageCount = (uint32_t) shaderStages.size(),
|
||||
.pStages = shaderStages.data(),
|
||||
.pVertexInputState = &vi,
|
||||
.pInputAssemblyState = &ia,
|
||||
.pViewportState = &vp,
|
||||
.pRasterizationState = &rs,
|
||||
.pMultisampleState = &ms,
|
||||
.pColorBlendState = &cb,
|
||||
.pDynamicState = &ds,
|
||||
.layout = layout
|
||||
};
|
||||
|
||||
VkPipeline pipeline;
|
||||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //V_RENDERER_H
|
||||
@@ -0,0 +1,452 @@
|
||||
//
|
||||
// Created by Vicente Ferrari Smith on 12.02.26.
|
||||
//
|
||||
|
||||
#define VOLK_IMPLEMENTATION
|
||||
#include <Volk/volk.h>
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
|
||||
#include "init.h"
|
||||
#include <print>
|
||||
#include <vector>
|
||||
|
||||
#include "../graphics.h"
|
||||
|
||||
VkInstance instance{};
|
||||
VkPhysicalDevice physicalDevice{};
|
||||
VkDevice device{};
|
||||
VkQueue graphics_queue{};
|
||||
uint32_t queueFamily{};
|
||||
|
||||
VkSurfaceKHR surface{};
|
||||
VkDebugUtilsMessengerEXT debugMessenger{};
|
||||
|
||||
VmaAllocator allocator{};
|
||||
|
||||
constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||
constexpr uint32_t MAX_VERTICES_PER_BATCH = 65536;
|
||||
|
||||
VkSwapchainKHR swapchain;
|
||||
VkExtent2D swapchain_extent;
|
||||
VkSurfaceFormatKHR swapchain_format{
|
||||
VK_FORMAT_B8G8R8A8_UNORM,
|
||||
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
};
|
||||
std::vector<VkSemaphore> renderFinished;
|
||||
|
||||
std::vector<VkImage> images;
|
||||
std::vector<VkImageView> imageViews;
|
||||
std::vector<VkImageLayout> imageLayouts;
|
||||
|
||||
void graphics_init() {
|
||||
createInstance(window);
|
||||
createSurface(window);
|
||||
createDevice();
|
||||
|
||||
createSwapchain(window);
|
||||
|
||||
slang::createGlobalSession(slangGlobalSession.writeRef());
|
||||
|
||||
Renderer renderer(window);
|
||||
texture_manager.load("assets/boy.png", renderer);
|
||||
}
|
||||
|
||||
void graphics_deinit() {
|
||||
vkDeviceWaitIdle(device);
|
||||
}
|
||||
|
||||
void begin_frame() {
|
||||
|
||||
}
|
||||
|
||||
void end_frame() {
|
||||
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
|
||||
void* userData) {
|
||||
const char* severityStr = "UNKNOWN";
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
|
||||
severityStr = "VERBOSE";
|
||||
else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
|
||||
severityStr = "INFO";
|
||||
else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
|
||||
severityStr = "WARNING";
|
||||
else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
||||
severityStr = "ERROR";
|
||||
|
||||
const char* typeStr = "";
|
||||
if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
|
||||
typeStr = "GENERAL";
|
||||
else if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
||||
typeStr = "VALIDATION";
|
||||
else if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
|
||||
typeStr = "PERFORMANCE";
|
||||
|
||||
std::fprintf(stderr, "\n[VULKAN %s][%s]\n%s\n",
|
||||
severityStr,
|
||||
typeStr,
|
||||
callbackData->pMessage);
|
||||
|
||||
// Print associated objects (very useful on MoltenVK)
|
||||
for (uint32_t i = 0; i < callbackData->objectCount; i++) {
|
||||
const VkDebugUtilsObjectNameInfoEXT& obj =
|
||||
callbackData->pObjects[i];
|
||||
|
||||
std::fprintf(stderr,
|
||||
" Object %u: type=%d handle=0x%llx name=%s\n",
|
||||
i,
|
||||
obj.objectType,
|
||||
static_cast<unsigned long long>(obj.objectHandle),
|
||||
obj.pObjectName ? obj.pObjectName : "null");
|
||||
}
|
||||
|
||||
return VK_FALSE; // Do not abort Vulkan call
|
||||
}
|
||||
|
||||
int createInstance(GLFWwindow *window) {
|
||||
volkInitialize();
|
||||
|
||||
uint32_t extensions_count;
|
||||
const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count);
|
||||
std::vector<const char*> instance_exts;
|
||||
|
||||
for (uint32_t i = 0; i < extensions_count; i++) {
|
||||
instance_exts.push_back(extensions[i]);
|
||||
}
|
||||
// Append portability enumeration (MANDATORY on macOS)
|
||||
instance_exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
instance_exts.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
|
||||
|
||||
uint32_t availableCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableCount, nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> available(availableCount);
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableCount, available.data());
|
||||
|
||||
bool has_portability_extension = false;
|
||||
|
||||
for (const VkExtensionProperties &property : available) {
|
||||
if (strcmp(property.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) == 0) {
|
||||
has_portability_extension = true;
|
||||
instance_exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr VkApplicationInfo app{
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.apiVersion = VK_API_VERSION_1_4,
|
||||
};
|
||||
|
||||
const char* layers[] = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT dbg{};
|
||||
dbg.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
dbg.messageSeverity =
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
dbg.messageType =
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
dbg.pfnUserCallback = debugCallback;
|
||||
|
||||
|
||||
VkInstanceCreateInfo ici{
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = &dbg,
|
||||
.pApplicationInfo = &app,
|
||||
.enabledLayerCount = 1,
|
||||
.ppEnabledLayerNames = layers,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(instance_exts.size()),
|
||||
.ppEnabledExtensionNames = instance_exts.data(),
|
||||
};
|
||||
|
||||
if (has_portability_extension) {
|
||||
ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
|
||||
VkResult res = vkCreateInstance(&ici, nullptr, &instance);
|
||||
if (res != VK_SUCCESS) {
|
||||
printf("vkCreateInstance failed: %d\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
volkLoadInstance(instance);
|
||||
|
||||
VkDebugUtilsMessengerEXT messenger;
|
||||
vkCreateDebugUtilsMessengerEXT(instance, &dbg, nullptr, &messenger);
|
||||
|
||||
|
||||
uint32_t layerCount = 0;
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void createSurface(GLFWwindow *window) {
|
||||
glfwCreateWindowSurface(instance, window, nullptr, &surface);
|
||||
}
|
||||
|
||||
void createDevice() {
|
||||
uint32_t deviceCount = 0;
|
||||
uint32_t res = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
||||
if (res != VK_SUCCESS || deviceCount == 0) {
|
||||
printf("vkEnumeratePhysicalDevices failed or found no devices (%d)\n", res);
|
||||
}
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
||||
|
||||
printf("Found %u physical device(s)\n", deviceCount);
|
||||
for (uint32_t i = 0; i < deviceCount; i++) {
|
||||
VkPhysicalDeviceProperties props{};
|
||||
vkGetPhysicalDeviceProperties(devices[i], &props);
|
||||
|
||||
printf("Device %u:\n", i);
|
||||
printf(" Name: %s\n", props.deviceName);
|
||||
printf(" Type: %u\n", props.deviceType);
|
||||
printf(" API version: %u.%u.%u\n",
|
||||
VK_VERSION_MAJOR(props.apiVersion),
|
||||
VK_VERSION_MINOR(props.apiVersion),
|
||||
VK_VERSION_PATCH(props.apiVersion));
|
||||
}
|
||||
|
||||
physicalDevice = devices[0];
|
||||
|
||||
queueFamily = 0;
|
||||
uint32_t qCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &qCount, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> qprops(qCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &qCount, qprops.data());
|
||||
|
||||
for (uint32_t i = 0; i < qCount; i++) {
|
||||
VkBool32 present = VK_FALSE;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &present);
|
||||
if ((qprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) {
|
||||
queueFamily = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (int r = glfwGetPhysicalDevicePresentationSupport(instance, physicalDevice, queueFamily) == GLFW_FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::println("{}", queueFamily);
|
||||
|
||||
std::vector<const char*> devExts = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
uint32_t extensionCount = 0;
|
||||
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
|
||||
|
||||
for (const auto& ext : availableExtensions) {
|
||||
if (strcmp(ext.extensionName, "VK_KHR_portability_subset") == 0) {
|
||||
devExts.push_back("VK_KHR_portability_subset");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float prio = 1.0f;
|
||||
VkDeviceQueueCreateInfo qci{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = queueFamily,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &prio
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan14Features enabledVk14Features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES,
|
||||
.pNext = nullptr,
|
||||
.globalPriorityQuery = false,
|
||||
.shaderSubgroupRotate = false,
|
||||
.shaderSubgroupRotateClustered = false,
|
||||
.shaderFloatControls2 = false,
|
||||
.shaderExpectAssume = false,
|
||||
.rectangularLines = false,
|
||||
.bresenhamLines = false,
|
||||
.smoothLines = false,
|
||||
.stippledRectangularLines = false,
|
||||
.stippledBresenhamLines = false,
|
||||
.stippledSmoothLines = false,
|
||||
.vertexAttributeInstanceRateDivisor = false,
|
||||
.vertexAttributeInstanceRateZeroDivisor = false,
|
||||
.indexTypeUint8 = false,
|
||||
.dynamicRenderingLocalRead = false,
|
||||
.maintenance5 = false,
|
||||
.maintenance6 = false,
|
||||
.pipelineProtectedAccess = false,
|
||||
.pipelineRobustness = false,
|
||||
.hostImageCopy = false,
|
||||
.pushDescriptor = false,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan13Features enabledVk13Features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
||||
.pNext = &enabledVk14Features,
|
||||
.robustImageAccess = false,
|
||||
.inlineUniformBlock = false,
|
||||
.descriptorBindingInlineUniformBlockUpdateAfterBind = false,
|
||||
.pipelineCreationCacheControl = false,
|
||||
.privateData = false,
|
||||
.shaderDemoteToHelperInvocation = false,
|
||||
.shaderTerminateInvocation = false,
|
||||
.subgroupSizeControl = false,
|
||||
.computeFullSubgroups = false,
|
||||
.synchronization2 = true,
|
||||
.textureCompressionASTC_HDR = false,
|
||||
.shaderZeroInitializeWorkgroupMemory = false,
|
||||
.dynamicRendering = true,
|
||||
.shaderIntegerDotProduct = false,
|
||||
.maintenance4 = false
|
||||
};
|
||||
VkPhysicalDeviceVulkan12Features enabledVk12Features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &enabledVk13Features,
|
||||
.descriptorIndexing = true,
|
||||
.shaderSampledImageArrayNonUniformIndexing = true,
|
||||
.descriptorBindingSampledImageUpdateAfterBind = true,
|
||||
.descriptorBindingPartiallyBound = true,
|
||||
.descriptorBindingVariableDescriptorCount = true,
|
||||
.runtimeDescriptorArray = true,
|
||||
.bufferDeviceAddress = true,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan11Features enabledVk11Features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.pNext = &enabledVk12Features,
|
||||
.shaderDrawParameters = true,
|
||||
};
|
||||
|
||||
const VkPhysicalDeviceFeatures enabledVk10Features{
|
||||
.samplerAnisotropy = true,
|
||||
};
|
||||
|
||||
const VkDeviceCreateInfo dci{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = &enabledVk11Features,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &qci,
|
||||
.enabledExtensionCount = (uint32_t)devExts.size(),
|
||||
.ppEnabledExtensionNames = devExts.data(),
|
||||
.pEnabledFeatures = &enabledVk10Features,
|
||||
};
|
||||
|
||||
vkCreateDevice(physicalDevice, &dci, nullptr, &device);
|
||||
|
||||
volkLoadDevice(device);
|
||||
|
||||
vkGetDeviceQueue(device, queueFamily, 0, &graphics_queue);
|
||||
|
||||
VmaAllocatorCreateInfo allocatorCreateInfo = {
|
||||
.flags = 0,
|
||||
.physicalDevice = physicalDevice,
|
||||
.device = device,
|
||||
.preferredLargeHeapBlockSize = 0,
|
||||
.pAllocationCallbacks = nullptr,
|
||||
.pDeviceMemoryCallbacks = nullptr,
|
||||
.pHeapSizeLimit = nullptr,
|
||||
.pVulkanFunctions = nullptr,
|
||||
.instance = instance,
|
||||
.vulkanApiVersion = VK_API_VERSION_1_4,
|
||||
.pTypeExternalMemoryHandleTypes = nullptr,
|
||||
};
|
||||
|
||||
VmaVulkanFunctions vulkan_functions = {};
|
||||
res = vmaImportVulkanFunctionsFromVolk(&allocatorCreateInfo, &vulkan_functions);
|
||||
|
||||
allocatorCreateInfo.pVulkanFunctions = &vulkan_functions;
|
||||
|
||||
res = vmaCreateAllocator(&allocatorCreateInfo, &allocator);
|
||||
}
|
||||
|
||||
void createSwapchain(GLFWwindow* window) {
|
||||
int fbWidth, fbHeight;
|
||||
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
|
||||
|
||||
swapchain_extent = {
|
||||
static_cast<uint32_t>(fbWidth),
|
||||
static_cast<uint32_t>(fbHeight)
|
||||
};
|
||||
|
||||
VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
|
||||
.pNext = nullptr,
|
||||
.surface = surface,
|
||||
};
|
||||
|
||||
VkSurfaceCapabilities2KHR surfCapabilities{
|
||||
.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
|
||||
};
|
||||
|
||||
vkGetPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &surfaceInfo, &surfCapabilities);
|
||||
|
||||
const VkSwapchainCreateInfoKHR sci{
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.surface = surface,
|
||||
.minImageCount = surfCapabilities.surfaceCapabilities.minImageCount,
|
||||
.imageFormat = swapchain_format.format,
|
||||
.imageColorSpace = swapchain_format.colorSpace,
|
||||
.imageExtent = swapchain_extent,
|
||||
.imageArrayLayers = 1,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
|
||||
.clipped = VK_TRUE
|
||||
};
|
||||
|
||||
vkCreateSwapchainKHR(device, &sci, nullptr, &swapchain);
|
||||
|
||||
uint32_t imgCount;
|
||||
vkGetSwapchainImagesKHR(device, swapchain, &imgCount, nullptr);
|
||||
std::print("imgCount: {}", imgCount);
|
||||
images = std::vector<VkImage>(imgCount);
|
||||
vkGetSwapchainImagesKHR(device, swapchain, &imgCount, images.data());
|
||||
|
||||
imageLayouts = std::vector<VkImageLayout>(
|
||||
imgCount,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED
|
||||
);
|
||||
|
||||
imageViews = std::vector<VkImageView>(imgCount);
|
||||
|
||||
const VkSemaphoreCreateInfo seci{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
};
|
||||
|
||||
renderFinished.resize(imgCount);
|
||||
|
||||
for (uint32_t i = 0; i < imgCount; i++) {
|
||||
VkImageViewCreateInfo ivci{};
|
||||
ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
ivci.image = images[i];
|
||||
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
ivci.format = swapchain_format.format; // must match swapchain format
|
||||
ivci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
ivci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
ivci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
ivci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
ivci.subresourceRange.baseMipLevel = 0;
|
||||
ivci.subresourceRange.levelCount = 1;
|
||||
ivci.subresourceRange.baseArrayLayer = 0;
|
||||
ivci.subresourceRange.layerCount = 1;
|
||||
|
||||
vkCreateImageView(device, &ivci, nullptr, &imageViews[i]);
|
||||
|
||||
vkCreateSemaphore(device, &seci, nullptr, &renderFinished[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by Vicente Ferrari Smith on 12.02.26.
|
||||
//
|
||||
|
||||
#ifndef V_INIT_H
|
||||
#define V_INIT_H
|
||||
|
||||
#include <volk/volk.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
#include <vector>
|
||||
|
||||
struct Device {
|
||||
VkDevice device;
|
||||
};
|
||||
|
||||
void createSwapchain(GLFWwindow* window);
|
||||
|
||||
int createInstance(GLFWwindow* window);
|
||||
void createSurface(GLFWwindow* window);
|
||||
void pickPhysicalDevice();
|
||||
void createDevice();
|
||||
|
||||
|
||||
#endif //V_INIT_H
|
||||
Reference in new issue
Block a user