From 8400d9f6e3d192a7ecad72f40caf496b0bae1cfa Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 12 Nov 2024 20:01:59 -0600 Subject: [PATCH] Can draw some text --- bootstrap/bloat-buster/font.c | 11 +- bootstrap/bloat-buster/gui.c | 324 ++++++++++++++++---------- bootstrap/bloat-buster/image_loader.c | 1 + bootstrap/include/bloat-buster/font.h | 7 + bootstrap/shaders/font.frag | 17 ++ bootstrap/shaders/font.vert | 41 ++++ 6 files changed, 270 insertions(+), 131 deletions(-) create mode 100644 bootstrap/shaders/font.frag create mode 100644 bootstrap/shaders/font.vert diff --git a/bootstrap/bloat-buster/font.c b/bootstrap/bloat-buster/font.c index 7000dbd..1a53658 100644 --- a/bootstrap/bloat-buster/font.c +++ b/bootstrap/bloat-buster/font.c @@ -7,7 +7,7 @@ TextureAtlas font_create_texture_atlas(Arena* arena, String font_path) { auto font_file = file_read(arena, font_path); stbtt_fontinfo font_info; - if (!stbtt_InitFont(&font_info, font_file.pointer, 0)) + if (!stbtt_InitFont(&font_info, font_file.pointer, stbtt_GetFontOffsetForIndex(font_file.pointer, 0))) { failed_execution(); } @@ -25,7 +25,7 @@ TextureAtlas font_create_texture_atlas(Arena* arena, String font_path) float scale_x = 0.0f; float scale_y = stbtt_ScaleForPixelHeight(&font_info, char_height); - for (u32 i = 0; i <= 256; ++i) + for (u32 i = 0; i < 256; ++i) { int width; int height; @@ -40,11 +40,14 @@ TextureAtlas font_create_texture_atlas(Arena* arena, String font_path) { for (int i = 0; i < width; ++i) { - atlas[(y + j) * atlas_width + (x + i)] = bitmap[j * width + i]; + auto atlas_index = (y + j) * atlas_width + (x + i); + auto bitmap_index = (height - j - 1) * width + i; + assert(atlas_index < atlas_size); + atlas[atlas_index] = bitmap[bitmap_index]; } } - stbtt_FreeBitmap(bitmap, nullptr); + stbtt_FreeBitmap(bitmap, 0); } x += char_width; diff --git a/bootstrap/bloat-buster/gui.c b/bootstrap/bloat-buster/gui.c index 6fa256c..202b8df 100644 --- a/bootstrap/bloat-buster/gui.c +++ b/bootstrap/bloat-buster/gui.c @@ -13,6 +13,9 @@ [[noreturn]] [[gnu::cold]] fn void wrong_vulkan_result(VkResult result, String call_string, String file, int line) { unused(result); + unused(call_string); + unused(file); + unused(line); trap(); } @@ -70,7 +73,7 @@ fn VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT mes unused(message_severity); unused(message_type); unused(user_data); - print("Validation message ({cstr}): {cstr}\n", callback_data->pMessageIdName, callback_data->pMessage); + print("Validation message ({cstr}): {cstr}\n", callback_data->pMessageIdName ? callback_data->pMessageIdName : "ID_NONE", callback_data->pMessage); if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { failed_execution(); @@ -412,13 +415,12 @@ fn VulkanImage vk_image_create(VkDevice device, const VkAllocationCallbacks* all STRUCT(ImageFromTextureOptions) { - u8 apply_gamma_correction; + VkFormat format; }; -fn VulkanImage vk_image_from_texture(VkDevice device, const VkAllocationCallbacks* allocation_callbacks, ImmediateContext immediate_context, VkPhysicalDeviceMemoryProperties memory_properties, TextureMemory texture, ImageFromTextureOptions options) +fn VulkanImage vk_image_from_texture(VkDevice device, const VkAllocationCallbacks* allocation_callbacks, ImmediateContext immediate_context, VkPhysicalDeviceMemoryProperties memory_properties, TextureMemory texture, VkFormat format) { assert(texture.depth == 1); - VkFormat format = options.apply_gamma_correction ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; VulkanImage result = vk_image_create(device, allocation_callbacks, memory_properties, (VulkanImageCreate) { .width = texture.width, @@ -428,8 +430,7 @@ fn VulkanImage vk_image_from_texture(VkDevice device, const VkAllocationCallback .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, }); - u32 channel_count = 4; - auto image_size = (u64)texture.depth * texture.width * texture.height * channel_count; + auto image_size = (u64)texture.depth * texture.width * texture.height * texture.channel_count; VkBufferUsageFlags buffer_usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; VkMemoryPropertyFlags buffer_memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; auto transfer_buffer = vk_buffer_create(device, allocation_callbacks, memory_properties, image_size, buffer_usage_flags, buffer_memory_flags); @@ -530,7 +531,7 @@ void run_app(Arena* arena) VkValidationFeatureEnableEXT enabled_validation_features[] = { - VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT, + // VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT, }; VkValidationFeaturesEXT validation_features = { @@ -892,6 +893,13 @@ void run_app(Arena* arena) .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = 0, }, + // { + // .binding = 1, + // .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + // .descriptorCount = 1, + // .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + // .pImmutableSamplers = 0, + // }, }; VkDescriptorSetLayout descriptor_set_layout; { @@ -910,8 +918,8 @@ void run_app(Arena* arena) VkPipeline graphics_pipeline; VkPipelineLayout graphics_pipeline_layout; { - VkShaderModule vertex_shader = vk_shader_module_create(arena, device, allocation_callbacks, strlit("bootstrap/shaders/quad.vert"), SHADER_STAGE_VERTEX); - VkShaderModule fragment_shader = vk_shader_module_create(arena, device, allocation_callbacks, strlit("bootstrap/shaders/quad_tex.frag"), SHADER_STAGE_FRAGMENT); + VkShaderModule vertex_shader = vk_shader_module_create(arena, device, allocation_callbacks, strlit("bootstrap/shaders/font.vert"), SHADER_STAGE_VERTEX); + VkShaderModule fragment_shader = vk_shader_module_create(arena, device, allocation_callbacks, strlit("bootstrap/shaders/font.frag"), SHADER_STAGE_FRAGMENT); VkPushConstantRange push_constant_ranges[] = { { @@ -1023,13 +1031,13 @@ void run_app(Arena* arena) VkPipelineColorBlendAttachmentState attachments[] = { { - .blendEnable = 0, - .srcColorBlendFactor = 0, - .dstColorBlendFactor = 0, - .colorBlendOp = 0, - .srcAlphaBlendFactor = 0, - .dstAlphaBlendFactor = 0, - .alphaBlendOp = 0, + .blendEnable = 1, + .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 = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }, }; @@ -1097,119 +1105,32 @@ void run_app(Arena* arena) vkok(vkCreateGraphicsPipelines(device, pipeline_cache, array_length(create_infos), create_infos, allocation_callbacks, &graphics_pipeline)); } - UNION(Vec3) - { - struct - { - f32 x, y, z; - }; - f32 v[3]; - }; - UNION(Vec4) - { - struct - { - f32 x, y, z, w; - }; - f32 v[4]; - }; - - STRUCT(Vertex) - { - f32 x; - f32 y; - f32 uv_x; - f32 uv_y; - }; - - auto width_float = (f32)initial_width; - auto height_float = (f32)initial_height; - Vertex vertices[] = { - { - .x = (3 * width_float) / 4, - .y = height_float / 4, - .uv_x = 1.0f, - .uv_y = 1.0f, - }, - { - .x = (3 * width_float) / 4, - .y = (3 * height_float) / 4, - .uv_x = 1.0f, - .uv_y = 0.0f, - }, - { - .x = width_float / 4, - .y = height_float / 4, - .uv_x = 0.0f, - .uv_y = 1.0f, - }, - { - .x = width_float / 4, - .y = (3 * height_float) / 4, - .uv_x = 0.0f, - .uv_y = 0.0f, - }, - }; - - u32 indices[] = { - 0, 1, 2, - 2, 1, 3 - }; - - VulkanBuffer vertex_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, sizeof(vertices), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VulkanBuffer index_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, sizeof(indices), VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VkBufferDeviceAddressInfo device_address_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = vertex_buffer.handle, - }; - VkDeviceAddress vertex_buffer_device_address = vkGetBufferDeviceAddress(device, &device_address_info); - - { - VulkanBuffer staging_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, vertex_buffer.size + index_buffer.size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - - memcpy(staging_buffer.cpu_data, vertices, sizeof(vertices)); - memcpy((u8*)staging_buffer.cpu_data + sizeof(vertices), indices, sizeof(indices)); - - immediate_start(immediate); - - VkBufferCopy vertex_copies[] = - { - { - .srcOffset = 0, - .dstOffset= 0, - .size = vertex_buffer.size, - } - }; - - vkCmdCopyBuffer(immediate.command_buffer, staging_buffer.handle, vertex_buffer.handle, array_length(vertex_copies), vertex_copies); - - VkBufferCopy index_copies[] = - { - { - .srcOffset = vertex_buffer.size, - .dstOffset= 0, - .size = index_buffer.size, - } - }; - - vkCmdCopyBuffer(immediate.command_buffer, staging_buffer.handle, index_buffer.handle, array_length(index_copies), index_copies); - - immediate_end(immediate); - } - - auto texture_atlas = font_create_texture_atlas(arena, strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf")); - - auto texture_path = + auto font_path = #ifdef _WIN32 - strlit("C:/Users/david/Pictures/buster.jpg"); +strlit("C:/Users/David/Downloads/Fira_Sans/FiraSans-Regular.ttf") #elif defined(__linux__) - strlit("/home/david/Pictures/buster.jpeg"); +strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf") #else #endif - auto texture = texture_load_from_file(arena, texture_path); - auto texture_image = vk_image_from_texture(device, allocation_callbacks, immediate, physical_device_memory_properties, texture, (ImageFromTextureOptions) { - .apply_gamma_correction = 0, - }); +; + auto texture_atlas = font_create_texture_atlas(arena, font_path); + auto texture_atlas_image = vk_image_from_texture(device, allocation_callbacks, immediate, physical_device_memory_properties, (TextureMemory) { + .pointer = texture_atlas.pointer, + .width = texture_atlas.width, + .height = texture_atlas.height, + .channel_count = 1, + .depth = 1, + }, VK_FORMAT_R8_UNORM); + +// auto texture_path = +// #ifdef _WIN32 +// strlit("C:/Users/david/Pictures/buster.jpg"); +// #elif defined(__linux__) +// strlit("/home/david/Pictures/buster.jpeg"); +// #else +// #endif +// auto texture = texture_load_from_file(arena, texture_path); +// auto texture_image = vk_image_from_texture(device, allocation_callbacks, immediate, physical_device_memory_properties, texture, VK_FORMAT_R8G8B8A8_SRGB); VkSampler sampler; { @@ -1273,7 +1194,7 @@ void run_app(Arena* arena) { .sampler = sampler, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = texture_image.view, + .imageView = texture_atlas_image.view, }, }; @@ -1297,6 +1218,155 @@ void run_app(Arena* arena) vkUpdateDescriptorSets(device, array_length(write_descriptor_sets), write_descriptor_sets, descriptor_copy_count, descriptor_copies); } + STRUCT(Vec4) + { + f32 v[4]; + }; + Vec4 color = {1, 1, 1, 1}; + static_assert(sizeof(color) == 4 * sizeof(float)); + + STRUCT(Vertex) + { + f32 x; + f32 y; + f32 uv_x; + f32 uv_y; + Vec4 color; + }; + + auto width_float = (f32)initial_width; + auto height_float = (f32)initial_height; + Vertex vertices[4*2]; + + u32 indices[] = { + 0, 1, 2, + 1, 3, 2, + 0 + 4, 1 + 4, 2 + 4, + 1 + 4, 3 + 4, 2 + 4 + }; + + { + u8 c = 'a'; + auto character_count_per_row = texture_atlas.width / texture_atlas.char_height; + auto row = c / character_count_per_row; + auto column = c % character_count_per_row; + auto pos_x = width_float / 2; + auto pos_y = height_float / 2; + auto uv_x = column * texture_atlas.char_height; + auto uv_y = row * texture_atlas.char_height; + + vertices[0] = (Vertex) { + .x = pos_x, + .y = pos_y, + .uv_x = (f32)uv_x / texture_atlas.width, + .uv_y = (f32)uv_y / texture_atlas.width, + .color = color, + }; + vertices[1] = (Vertex) { + .x = pos_x + texture_atlas.char_height, + .y = pos_y, + .uv_x = (f32)(uv_x + texture_atlas.char_height) / texture_atlas.width, + .uv_y = (f32)uv_y / texture_atlas.width, + .color = color, + }; + vertices[2] = (Vertex) { + .x = pos_x, + .y = pos_y + texture_atlas.char_height, + .uv_x = (f32)uv_x / texture_atlas.width, + .uv_y = (f32)(uv_y + texture_atlas.char_height) / texture_atlas.width, + .color = color, + }; + vertices[3] = (Vertex) { + .x = pos_x + texture_atlas.char_height, + .y = pos_y + texture_atlas.char_height, + .uv_x = (f32)(uv_x + texture_atlas.char_height) / texture_atlas.width, + .uv_y = (f32)(uv_y + texture_atlas.char_height) / texture_atlas.width, + .color = color, + }; + } + + { + u8 c = 'b'; + auto character_count_per_row = texture_atlas.width / texture_atlas.char_height; + auto row = c / character_count_per_row; + auto column = c % character_count_per_row; + auto pos_x = width_float / 2 + texture_atlas.char_height; + auto pos_y = height_float / 2; + auto uv_x = column * texture_atlas.char_height; + auto uv_y = row * texture_atlas.char_height; + + vertices[4] = (Vertex) { + .x = pos_x, + .y = pos_y, + .uv_x = (f32)uv_x / texture_atlas.width, + .uv_y = (f32)uv_y / texture_atlas.width, + .color = color, + }; + vertices[5] = (Vertex) { + .x = pos_x + texture_atlas.char_height, + .y = pos_y, + .uv_x = (f32)(uv_x + texture_atlas.char_height) / texture_atlas.width, + .uv_y = (f32)uv_y / texture_atlas.width, + .color = color, + }; + vertices[6] = (Vertex) { + .x = pos_x, + .y = pos_y + texture_atlas.char_height, + .uv_x = (f32)uv_x / texture_atlas.width, + .uv_y = (f32)(uv_y + texture_atlas.char_height) / texture_atlas.width, + .color = color, + }; + vertices[7] = (Vertex) { + .x = pos_x + texture_atlas.char_height, + .y = pos_y + texture_atlas.char_height, + .uv_x = (f32)(uv_x + texture_atlas.char_height) / texture_atlas.width, + .uv_y = (f32)(uv_y + texture_atlas.char_height) / texture_atlas.width, + .color = color, + }; + } + + VulkanBuffer vertex_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, sizeof(vertices), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VulkanBuffer index_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, sizeof(indices), VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VkBufferDeviceAddressInfo device_address_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = vertex_buffer.handle, + }; + VkDeviceAddress vertex_buffer_device_address = vkGetBufferDeviceAddress(device, &device_address_info); + + VulkanBuffer staging_buffer = vk_buffer_create(device, allocation_callbacks, physical_device_memory_properties, vertex_buffer.size + index_buffer.size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + + { + + memcpy(staging_buffer.cpu_data, vertices, sizeof(vertices)); + memcpy((u8*)staging_buffer.cpu_data + sizeof(vertices), indices, sizeof(indices)); + + immediate_start(immediate); + + VkBufferCopy vertex_copies[] = + { + { + .srcOffset = 0, + .dstOffset= 0, + .size = vertex_buffer.size, + } + }; + + vkCmdCopyBuffer(immediate.command_buffer, staging_buffer.handle, vertex_buffer.handle, array_length(vertex_copies), vertex_copies); + + VkBufferCopy index_copies[] = + { + { + .srcOffset = vertex_buffer.size, + .dstOffset= 0, + .size = index_buffer.size, + } + }; + + vkCmdCopyBuffer(immediate.command_buffer, staging_buffer.handle, index_buffer.handle, array_length(index_copies), index_copies); + + immediate_end(immediate); + } + u32 frame_completed = 0; for (u32 frame_number = 0; !glfwWindowShouldClose(window); frame_number += frame_completed) { @@ -1422,7 +1492,7 @@ void run_app(Arena* arena) VkIndexType index_type = VK_INDEX_TYPE_UINT32; vkCmdBindIndexBuffer(command_buffer, index_buffer.handle, index_buffer_offset, index_type); - vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0); + vkCmdDrawIndexed(command_buffer, array_length(indices), 1, 0, 0, 0); vkCmdEndRendering(command_buffer); diff --git a/bootstrap/bloat-buster/image_loader.c b/bootstrap/bloat-buster/image_loader.c index 6018c93..d041af7 100644 --- a/bootstrap/bloat-buster/image_loader.c +++ b/bootstrap/bloat-buster/image_loader.c @@ -10,6 +10,7 @@ EXPORT TextureMemory texture_load_from_file(Arena* arena, String path) int height; int channels; u8* buffer = stbi_load_from_memory(file.pointer, file.length, &width, &height, &channels, STBI_rgb_alpha); + channels += 1; return (TextureMemory) { .pointer = buffer, diff --git a/bootstrap/include/bloat-buster/font.h b/bootstrap/include/bloat-buster/font.h index d29043a..acc0001 100644 --- a/bootstrap/include/bloat-buster/font.h +++ b/bootstrap/include/bloat-buster/font.h @@ -1,11 +1,18 @@ #include #include +STRUCT(FontCharacter) +{ + int advance; +}; + STRUCT(TextureAtlas) { u8* pointer; + FontCharacter* characters; u32 width; u32 height; + u32 character_width; u32 char_height; }; diff --git a/bootstrap/shaders/font.frag b/bootstrap/shaders/font.frag new file mode 100644 index 0000000..4add626 --- /dev/null +++ b/bootstrap/shaders/font.frag @@ -0,0 +1,17 @@ +#version 450 + +//shader input +layout (location = 0) in vec2 in_uv; +layout (location = 1) in vec4 in_color; + +//output write +layout (location = 0) out vec4 out_frag_color; + +layout(set = 0, binding = 0) uniform sampler2D atlas_texture; + +void main() +{ + vec4 alpha_v = texture(atlas_texture, in_uv); + vec4 sampled = vec4(1.0, 1.0, 1.0, alpha_v.r); + out_frag_color = in_color * sampled; +} diff --git a/bootstrap/shaders/font.vert b/bootstrap/shaders/font.vert new file mode 100644 index 0000000..9d32ca7 --- /dev/null +++ b/bootstrap/shaders/font.vert @@ -0,0 +1,41 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_debug_printf : require + +layout (location = 0) out vec2 out_uv; +layout (location = 1) out vec4 out_color; + +struct Vertex { + float x; + float y; + float uv_x; + float uv_y; + vec4 color; +}; + +layout(buffer_reference, std430) readonly buffer VertexBuffer{ + Vertex vertices[]; +}; + +//push constants block +layout(push_constant) uniform constants +{ + VertexBuffer vertex_buffer; + float width; + float height; +} PushConstants; + +void main() +{ + //load vertex data from device address + Vertex v = PushConstants.vertex_buffer.vertices[gl_VertexIndex]; + float width = PushConstants.width; + float height = PushConstants.height; + + //output data + gl_Position = vec4(2 * v.x / width - 1, 1 - (2 * v.y) / height, 0.0, 1.0); + out_uv = vec2(v.uv_x, v.uv_y); + out_color = v.color; + //debugPrintfEXT("Position: (%f, %f, %f)\n", v.position.x, v.position.y, v.position.z); + //debugPrintfEXT("Color: (%f, %f, %f)\n", v.color.x, v.color.y, v.color.z); +}