to test on windows

This commit is contained in:
Vicente Ferrari Smith 2026-02-20 14:35:31 +01:00
parent b83207e652
commit a015aa00ef
10 changed files with 102 additions and 81 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 685 KiB

BIN
assets/boy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

@ -51,7 +51,7 @@ int main() {
Renderer renderer(window);
texture_manager.load("assets/boy.jpg", renderer);
texture_manager.load("assets/boy.png", renderer);
uint64_t current_time = glfwGetTimerValue();
@ -72,8 +72,8 @@ int main() {
t += dt;
updates++;
}
std::println("Updates: {}", updates);
std::println("frame time: {}", ((double) (frame_time) / (double) glfwGetTimerFrequency()));
// std::println("Updates: {}", updates);
// std::println("frame time: {}", ((double) (frame_time) / (double) glfwGetTimerFrequency()));
renderer.begin_frame();

View File

@ -102,39 +102,33 @@ void Renderer::submit_sprite(glm::vec2 pos, const sprite_t &sprite) {
}
void Renderer::create_pipeline_layout() {
VkDescriptorSetLayoutBinding bindings[2];
bindings[0] = VkDescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {
{
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = nextTextureSlot,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
}
}
};
bindings[1] = {
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT
};
VkDescriptorBindingFlags flags[2] = {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
0 // Standard behavior for the buffer
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 = 2,
.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 = 2,
.pBindings = bindings
// .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.bindingCount = bindings.size(),
.pBindings = bindings.data()
};
vkCreateDescriptorSetLayout(device, &dslci, nullptr, &descriptor_set_layout);
@ -316,15 +310,14 @@ VkPipeline Renderer::get_pipeline(PipelineType type) const {
void Renderer::create_descriptor_pool() {
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 },
{ 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 = 2,
.poolSizeCount = 1,
.pPoolSizes = pool_sizes
};
@ -340,7 +333,7 @@ void Renderer::create_descriptor_pool() {
vkAllocateDescriptorSets(device, &alloc_info, &set);
}
void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) {
void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const {
VkDescriptorImageInfo image_info{
.sampler = sampler,
.imageView = view,
@ -350,7 +343,7 @@ void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler s
VkWriteDescriptorSet write{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set,
.dstArrayElement = slot, // Index in the 1000-size array
.dstArrayElement = slot,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &image_info
@ -359,9 +352,16 @@ void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler s
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
}
Texture Renderer::upload_texture(int w, int h, void* pixels) {
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;
Texture res{};
// --- 1. Create Staging Buffer (CPU Visible) ---
VkBuffer stagingBuffer;
@ -371,9 +371,10 @@ Texture Renderer::upload_texture(int w, int h, void* pixels) {
stagingBufferInfo.size = imageSize;
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo stagingAllocCreateInfo = {};
stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_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);
@ -384,26 +385,30 @@ Texture Renderer::upload_texture(int w, int h, void* pixels) {
// --- 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 };
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageInfo.extent = imageExtent;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
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 = {};
imageAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
imageAllocCreateInfo.priority = 1.0f; // High priority for textures
VmaAllocationCreateInfo imageAllocCreateInfo = {
.usage = VMA_MEMORY_USAGE_AUTO,
.priority = 1.0f,
};
vmaCreateImage(allocator, &imageInfo, &imageAllocCreateInfo, &res.image, &res.allocation, nullptr);
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, res.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
transition_image_layout(cmd, *image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy copyRegion = {};
copyRegion.bufferOffset = 0;
@ -415,23 +420,21 @@ Texture Renderer::upload_texture(int w, int h, void* pixels) {
copyRegion.imageSubresource.layerCount = 1;
copyRegion.imageExtent = imageExtent;
vkCmdCopyBufferToImage(cmd, stagingBuffer, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);
vkCmdCopyBufferToImage(cmd, stagingBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);
// Transition image from TRANSFER_DST to SHADER_READ_ONLY
transition_image_layout(cmd, res.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
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 ---
res.view = create_image_view(res.image, imageInfo.format);
*view = create_image_view(*image, imageInfo.format);
// Register in your Bindless Array (Set 0, Binding 0, Index N)
res.descriptor_index = nextTextureSlot++;
update_bindless_slot(res.descriptor_index, res.view, defaultSampler);
return res;
*descriptor_index = nextTextureSlot++;
update_bindless_slot(*descriptor_index, *view, defaultSampler);
}
void Renderer::immediate_submit(std::function<void(VkCommandBuffer)>&& func) const {
@ -513,25 +516,27 @@ VkImageView Renderer::create_image_view(VkImage image, VkFormat format) const {
}
void Renderer::create_default_sampler() {
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
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,
// For crisp pixel art, use NEAREST. For smooth textures, use LINEAR.
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
// How to handle "out of bounds" UVs
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
// 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
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
// 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);
}

View File

@ -203,13 +203,20 @@ struct Renderer {
const Frame &frame,
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
VkPipeline get_pipeline(PipelineType type) 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);
void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
// Returns the resource info so the Manager can store it
Texture upload_texture(int w, int h, void* pixels);
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(

View File

@ -10,7 +10,7 @@ TextureManager::TextureManager() {
}
uint32_t TextureManager::load(const std::string& path, Renderer &renderer) {
Texture TextureManager::load(const std::string& path, Renderer &renderer) {
// Dedup: Don't load the same file twice!
// if (path_to_id.contains(path)) return path_to_id[path];
@ -18,13 +18,21 @@ uint32_t TextureManager::load(const std::string& path, Renderer &renderer) {
unsigned char* data = stbi_load(path.c_str(), &w, &h, &ch, STBI_rgb_alpha);
// Tell the renderer to make the GPU version
Texture res = renderer.upload_texture(w, h, data);
Texture res;
res.width = w;
res.height = h;
res.channels = STBI_rgb_alpha;
res.srgb = true;
renderer.upload_texture(w, h, data, &res.image, &res.allocation, &res.view, &res.descriptor_index);
stbi_image_free(data);
uint32_t id = static_cast<uint32_t>(textures.size());
res.id = path;
res.path = path;
res.uploaded = true;
textures[path] = res;
// path_to_id[path] = id;
return id; // This is the textureID for your sprites
return res; // This is the textureID for your sprites
}

View File

@ -17,7 +17,6 @@ typedef std::string texture_id;
struct Texture {
texture_id id;
std::string path;
uint32_t index;
// NOTE: (vfs) stb_image wants s32 values for the width and height.
int32_t width;
@ -37,7 +36,7 @@ struct TextureManager {
std::unordered_map<texture_id, Texture> textures;
TextureManager();
uint32_t load(const std::string& path, Renderer &renderer);
Texture load(const std::string& path, Renderer &renderer);
};
inline TextureManager texture_manager;

View File

@ -2,4 +2,4 @@
// Created by Vicente Ferrari Smith on 14.02.26.
//
#include "texture_sheet.h"
#include "texture_sheet.h"

View File

@ -17,6 +17,8 @@ struct VSOutput {
uint32_t tex_id;
};
Sampler2D textures[];
static const float2 square[6] = {
float2(-0.5, -0.5), // Top-left
float2(-0.5, 0.5), // Bottom-left