From b0adc3b8710bab4e67a28a001836b9d3d51cb3cc Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Sat, 30 Dec 2023 12:19:17 +0100 Subject: lots of things aaaaaaaa Signed-off-by: Anna (navi) Figueiredo Gomes --- Makefile | 4 +- linmath.h | 605 +++++++++++++++++++++++++++++++++++++++++++++++ main.c | 761 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- pfp.png | Bin 0 -> 455024 bytes shader.frag | 4 +- shader.vert | 26 +-- 6 files changed, 1339 insertions(+), 61 deletions(-) create mode 100644 linmath.h create mode 100644 pfp.png diff --git a/Makefile b/Makefile index d7a272c..b771781 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CFLAGS = -O2 -g -LDFLAGS != pkg-config --libs vulkan sdl2 +LDFLAGS != pkg-config --libs vulkan sdl2 libpng vulkan: main.c vert.spv frag.spv - gcc $(CFLAGS) -o vk main.c $(LDFLAGS) + gcc $(CFLAGS) -o vk main.c $(LDFLAGS) -lm %.spv: shader.% glslc $^ -o $@ diff --git a/linmath.h b/linmath.h new file mode 100644 index 0000000..dd329e1 --- /dev/null +++ b/linmath.h @@ -0,0 +1,605 @@ +#ifndef LINMATH_H +#define LINMATH_H + +#include +#include +#include + +#ifdef LINMATH_NO_INLINE +#define LINMATH_H_FUNC static +#else +#define LINMATH_H_FUNC static inline +#endif + +#define LINMATH_H_DEFINE_VEC(n) \ +typedef float vec##n[n]; \ +LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ +{ \ + int i; \ + for(i=0; ib[i] ? a[i] : b[i]; \ +} \ +LINMATH_H_FUNC void vec##n##_dup(vec##n r, vec##n const src) \ +{ \ + int i; \ + for(i=0; i 1e-4) { + vec3_norm(u, u); + mat4x4 T; + mat4x4_from_vec3_mul_outer(T, u, u); + + mat4x4 S = { + { 0, u[2], -u[1], 0}, + {-u[2], 0, u[0], 0}, + { u[1], -u[0], 0, 0}, + { 0, 0, 0, 0} + }; + mat4x4_scale(S, S, s); + + mat4x4 C; + mat4x4_identity(C); + mat4x4_sub(C, C, T); + + mat4x4_scale(C, C, c); + + mat4x4_add(T, T, C); + mat4x4_add(T, T, S); + + T[3][3] = 1.f; + mat4x4_mul(R, M, T); + } else { + mat4x4_dup(R, M); + } +} +LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = { + {1.f, 0.f, 0.f, 0.f}, + {0.f, c, s, 0.f}, + {0.f, -s, c, 0.f}, + {0.f, 0.f, 0.f, 1.f} + }; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = { + { c, 0.f, -s, 0.f}, + { 0.f, 1.f, 0.f, 0.f}, + { s, 0.f, c, 0.f}, + { 0.f, 0.f, 0.f, 1.f} + }; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = { + { c, s, 0.f, 0.f}, + { -s, c, 0.f, 0.f}, + { 0.f, 0.f, 1.f, 0.f}, + { 0.f, 0.f, 0.f, 1.f} + }; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 const M) +{ + float s[6]; + float c[6]; + s[0] = M[0][0]*M[1][1] - M[1][0]*M[0][1]; + s[1] = M[0][0]*M[1][2] - M[1][0]*M[0][2]; + s[2] = M[0][0]*M[1][3] - M[1][0]*M[0][3]; + s[3] = M[0][1]*M[1][2] - M[1][1]*M[0][2]; + s[4] = M[0][1]*M[1][3] - M[1][1]*M[0][3]; + s[5] = M[0][2]*M[1][3] - M[1][2]*M[0][3]; + + c[0] = M[2][0]*M[3][1] - M[3][0]*M[2][1]; + c[1] = M[2][0]*M[3][2] - M[3][0]*M[2][2]; + c[2] = M[2][0]*M[3][3] - M[3][0]*M[2][3]; + c[3] = M[2][1]*M[3][2] - M[3][1]*M[2][2]; + c[4] = M[2][1]*M[3][3] - M[3][1]*M[2][3]; + c[5] = M[2][2]*M[3][3] - M[3][2]*M[2][3]; + + /* Assumes it is invertible */ + float idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] ); + + T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet; + T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet; + T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet; + T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet; + + T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet; + T[1][1] = ( M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet; + T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet; + T[1][3] = ( M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet; + + T[2][0] = ( M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet; + T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet; + T[2][2] = ( M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet; + T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet; + + T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet; + T[3][1] = ( M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet; + T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet; + T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet; +} +LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 const M) +{ + mat4x4_dup(R, M); + float s = 1.f; + vec3 h; + + vec3_norm(R[2], R[2]); + + s = vec3_mul_inner(R[1], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[1], R[1], h); + vec3_norm(R[1], R[1]); + + s = vec3_mul_inner(R[0], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[0], R[0], h); + + s = vec3_mul_inner(R[0], R[1]); + vec3_scale(h, R[1], s); + vec3_sub(R[0], R[0], h); + vec3_norm(R[0], R[0]); +} + +LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f) +{ + M[0][0] = 2.f*n/(r-l); + M[0][1] = M[0][2] = M[0][3] = 0.f; + + M[1][1] = 2.f*n/(t-b); + M[1][0] = M[1][2] = M[1][3] = 0.f; + + M[2][0] = (r+l)/(r-l); + M[2][1] = (t+b)/(t-b); + M[2][2] = -(f+n)/(f-n); + M[2][3] = -1.f; + + M[3][2] = -2.f*(f*n)/(f-n); + M[3][0] = M[3][1] = M[3][3] = 0.f; +} +LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f) +{ + M[0][0] = 2.f/(r-l); + M[0][1] = M[0][2] = M[0][3] = 0.f; + + M[1][1] = 2.f/(t-b); + M[1][0] = M[1][2] = M[1][3] = 0.f; + + M[2][2] = -2.f/(f-n); + M[2][0] = M[2][1] = M[2][3] = 0.f; + + M[3][0] = -(r+l)/(r-l); + M[3][1] = -(t+b)/(t-b); + M[3][2] = -(f+n)/(f-n); + M[3][3] = 1.f; +} +LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f) +{ + /* NOTE: Degrees are an unhandy unit to work with. + * linmath.h uses radians for everything! */ + float const a = 1.f / tanf(y_fov / 2.f); + + m[0][0] = a / aspect; + m[0][1] = 0.f; + m[0][2] = 0.f; + m[0][3] = 0.f; + + m[1][0] = 0.f; + m[1][1] = a; + m[1][2] = 0.f; + m[1][3] = 0.f; + + m[2][0] = 0.f; + m[2][1] = 0.f; + m[2][2] = -((f + n) / (f - n)); + m[2][3] = -1.f; + + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = -((2.f * f * n) / (f - n)); + m[3][3] = 0.f; +} +LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 const eye, vec3 const center, vec3 const up) +{ + /* Adapted from Android's OpenGL Matrix.java. */ + /* See the OpenGL GLUT documentation for gluLookAt for a description */ + /* of the algorithm. We implement it in a straightforward way: */ + + /* TODO: The negation of of can be spared by swapping the order of + * operands in the following cross products in the right way. */ + vec3 f; + vec3_sub(f, center, eye); + vec3_norm(f, f); + + vec3 s; + vec3_mul_cross(s, f, up); + vec3_norm(s, s); + + vec3 t; + vec3_mul_cross(t, s, f); + + m[0][0] = s[0]; + m[0][1] = t[0]; + m[0][2] = -f[0]; + m[0][3] = 0.f; + + m[1][0] = s[1]; + m[1][1] = t[1]; + m[1][2] = -f[1]; + m[1][3] = 0.f; + + m[2][0] = s[2]; + m[2][1] = t[2]; + m[2][2] = -f[2]; + m[2][3] = 0.f; + + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = 0.f; + m[3][3] = 1.f; + + mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]); +} + +typedef float quat[4]; +#define quat_add vec4_add +#define quat_sub vec4_sub +#define quat_norm vec4_norm +#define quat_scale vec4_scale +#define quat_mul_inner vec4_mul_inner + +LINMATH_H_FUNC void quat_identity(quat q) +{ + q[0] = q[1] = q[2] = 0.f; + q[3] = 1.f; +} +LINMATH_H_FUNC void quat_mul(quat r, quat const p, quat const q) +{ + vec3 w, tmp; + + vec3_mul_cross(tmp, p, q); + vec3_scale(w, p, q[3]); + vec3_add(tmp, tmp, w); + vec3_scale(w, q, p[3]); + vec3_add(tmp, tmp, w); + + vec3_dup(r, tmp); + r[3] = p[3]*q[3] - vec3_mul_inner(p, q); +} +LINMATH_H_FUNC void quat_conj(quat r, quat const q) +{ + int i; + for(i=0; i<3; ++i) + r[i] = -q[i]; + r[3] = q[3]; +} +LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 const axis) { + vec3 axis_norm; + vec3_norm(axis_norm, axis); + float s = sinf(angle / 2); + float c = cosf(angle / 2); + vec3_scale(r, axis_norm, s); + r[3] = c; +} +LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat const q, vec3 const v) +{ +/* + * Method by Fabian 'ryg' Giessen (of Farbrausch) +t = 2 * cross(q.xyz, v) +v' = v + q.w * t + cross(q.xyz, t) + */ + vec3 t; + vec3 q_xyz = {q[0], q[1], q[2]}; + vec3 u = {q[0], q[1], q[2]}; + + vec3_mul_cross(t, q_xyz, v); + vec3_scale(t, t, 2); + + vec3_mul_cross(u, q_xyz, t); + vec3_scale(t, t, q[3]); + + vec3_add(r, v, t); + vec3_add(r, r, u); +} +LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat const q) +{ + float a = q[3]; + float b = q[0]; + float c = q[1]; + float d = q[2]; + float a2 = a*a; + float b2 = b*b; + float c2 = c*c; + float d2 = d*d; + + M[0][0] = a2 + b2 - c2 - d2; + M[0][1] = 2.f*(b*c + a*d); + M[0][2] = 2.f*(b*d - a*c); + M[0][3] = 0.f; + + M[1][0] = 2*(b*c - a*d); + M[1][1] = a2 - b2 + c2 - d2; + M[1][2] = 2.f*(c*d + a*b); + M[1][3] = 0.f; + + M[2][0] = 2.f*(b*d + a*c); + M[2][1] = 2.f*(c*d - a*b); + M[2][2] = a2 - b2 - c2 + d2; + M[2][3] = 0.f; + + M[3][0] = M[3][1] = M[3][2] = 0.f; + M[3][3] = 1.f; +} + +LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 const M, quat const q) +{ +/* XXX: The way this is written only works for orthogonal matrices. */ +/* TODO: Take care of non-orthogonal case. */ + quat_mul_vec3(R[0], q, M[0]); + quat_mul_vec3(R[1], q, M[1]); + quat_mul_vec3(R[2], q, M[2]); + + R[3][0] = R[3][1] = R[3][2] = 0.f; + R[0][3] = M[0][3]; + R[1][3] = M[1][3]; + R[2][3] = M[2][3]; + R[3][3] = M[3][3]; // typically 1.0, but here we make it general +} +LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 const M) +{ + float r=0.f; + int i; + + int perm[] = { 0, 1, 2, 0, 1 }; + int *p = perm; + + for(i = 0; i<3; i++) { + float m = M[i][i]; + if( m < r ) + continue; + m = r; + p = &perm[i]; + } + + r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] ); + + if(r < 1e-6) { + q[0] = 1.f; + q[1] = q[2] = q[3] = 0.f; + return; + } + + q[0] = r/2.f; + q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.f*r); + q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.f*r); + q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.f*r); +} + +LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 const _b, float s) +{ + vec2 a; memcpy(a, _a, sizeof(a)); + vec2 b; memcpy(b, _b, sizeof(b)); + + float z_a = 0.; + float z_b = 0.; + + if(vec2_len(a) < 1.) { + z_a = sqrtf(1. - vec2_mul_inner(a, a)); + } else { + vec2_norm(a, a); + } + + if(vec2_len(b) < 1.) { + z_b = sqrtf(1. - vec2_mul_inner(b, b)); + } else { + vec2_norm(b, b); + } + + vec3 a_ = {a[0], a[1], z_a}; + vec3 b_ = {b[0], b[1], z_b}; + + vec3 c_; + vec3_mul_cross(c_, a_, b_); + + float const angle = acos(vec3_mul_inner(a_, b_)) * s; + mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle); +} +#endif diff --git a/main.c b/main.c index 7063979..b3648cb 100644 --- a/main.c +++ b/main.c @@ -1,12 +1,90 @@ +#define STB_IMAGE_IMPLEMENTATION + #include #include #include #include #include #include +#include +#include "linmath.h" +#include "stb_image.h" #define MAX_FRAMES_INFLIGHT 2 +typedef mat4x4 mat4; + +struct ubo { + mat4 model; + mat4 view; + mat4 proj; +}; + +struct vertex { + vec3 pos; + vec3 color; + vec2 texture_coords; +}; + +struct vertex vertices[] = { + {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, + + {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, + +/* + {{ 0.0f, -0.5f}, {0.445f, 0.09f, 0.727f}}, + + {{-0.5f, -1.0f}, {0.445f, 0.09f, 0.727f}}, + {{-1.0f, -0.5f}, {0.445f, 0.09f, 0.727f}}, + + {{ 1.0f, -0.5f}, {0.445f, 0.09f, 0.727f}}, + {{ 0.5f, -1.0f}, {0.445f, 0.09f, 0.727f}}, + + {{ 0.0f, 1.0f}, {0.445f, 0.09f, 0.727f}}, +*/ +}; + +uint16_t indices[] = { + 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4 +}; +// uint16_t indices[] = {1, 0, 2, 3, 0, 4, 2, 3, 5}; + +VkVertexInputBindingDescription get_vertex_description() { + return (VkVertexInputBindingDescription) { + .binding = 0, + .stride = sizeof(struct vertex), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX + }; +} + +void get_attribute_description(VkVertexInputAttributeDescription out[3]) { + out[0] = (VkVertexInputAttributeDescription) { + .binding = 0, + .location = 0, + .format = VK_FORMAT_R32G32B32_SFLOAT, + .offset = offsetof(struct vertex, pos) + }; + out[1] = (VkVertexInputAttributeDescription) { + .binding = 0, + .location = 1, + .format = VK_FORMAT_R32G32B32_SFLOAT, + .offset = offsetof(struct vertex, color) + }; + out[2] = (VkVertexInputAttributeDescription) { + .binding = 0, + .location = 2, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(struct vertex, texture_coords) + }; +} + static SDL_Window *window = NULL; static VkDebugUtilsMessengerEXT debug_messenger; static VkInstance instance = VK_NULL_HANDLE; @@ -19,6 +97,7 @@ static VkSwapchainKHR swapchain; static VkFormat swapchain_format; static VkExtent2D swapchain_extent; static VkRenderPass render_pass; +static VkDescriptorSetLayout desc_layout; static VkPipelineLayout pipeline_layout; static VkCommandPool command_pool; static VkCommandBuffer command_buffers[MAX_FRAMES_INFLIGHT]; @@ -26,7 +105,23 @@ static VkPipeline graphics_pipeline; static VkSemaphore image_avail[MAX_FRAMES_INFLIGHT]; static VkSemaphore render_finished[MAX_FRAMES_INFLIGHT]; static VkFence in_flight_fence[MAX_FRAMES_INFLIGHT]; +static VkBuffer uniform_buffers[MAX_FRAMES_INFLIGHT]; +static VkDeviceMemory uniform_memory[MAX_FRAMES_INFLIGHT]; +static void *mapped_buffers[MAX_FRAMES_INFLIGHT]; +static VkDescriptorPool desc_pool; +static VkDescriptorSet desc_sets[MAX_FRAMES_INFLIGHT]; static uint32_t current_frame = 0; +static VkBuffer vertex_buffer; +static VkDeviceMemory vertex_buffer_memory; +static VkBuffer index_buffer; +static VkDeviceMemory index_buffer_memroy; +static VkImage texture_image; +static VkDeviceMemory texture_image_memory; +static VkImageView texture_view; +static VkSampler texture_sampler; +static VkImage depth_image; +static VkDeviceMemory depth_memory; +static VkImageView depth_image_view; static struct { size_t len; VkImage data[]; @@ -131,6 +226,9 @@ bool found_queue_indx(struct queue_indices inds) { } void cleanup_swapchain() { + vkDestroyImageView(gpu, depth_image_view, NULL); + vkDestroyImage(gpu, depth_image, NULL); + vkFreeMemory(gpu, depth_memory, NULL); for (size_t i = 0; i < framebuffers->len; i++) { vkDestroyFramebuffer(gpu, framebuffers->data[i], NULL); } @@ -146,13 +244,37 @@ void cleanup() { vkDestroySemaphore(gpu, image_avail[i], NULL); vkDestroySemaphore(gpu, render_finished[i], NULL); vkDestroyFence(gpu, in_flight_fence[i], NULL); + + vkDestroyBuffer(gpu, uniform_buffers[i], NULL); + vkFreeMemory(gpu, uniform_memory[i], NULL); } cleanup_swapchain(); + + vkDestroyBuffer(gpu, index_buffer, NULL); + vkFreeMemory(gpu, index_buffer_memroy, NULL); + + vkDestroySampler(gpu, texture_sampler, NULL); + vkDestroyImageView(gpu, texture_view, NULL); + vkDestroyImage(gpu, texture_image, NULL); + vkFreeMemory(gpu, texture_image_memory, NULL); + + vkDestroyDescriptorPool(gpu, desc_pool, NULL); + vkDestroyDescriptorSetLayout(gpu, desc_layout, NULL); + + vkDestroyBuffer(gpu, vertex_buffer, NULL); + vkFreeMemory(gpu, vertex_buffer_memory, NULL); + vkDestroyCommandPool(gpu, command_pool, NULL); + vkDestroyPipeline(gpu, graphics_pipeline, NULL); vkDestroyPipelineLayout(gpu, pipeline_layout, NULL); + vkDestroyRenderPass(gpu, render_pass, NULL); vkDestroySurfaceKHR(instance, surface, NULL); + + ((PFN_vkDestroyDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"))(instance, debug_messenger, NULL); + vkDestroyDevice(gpu, NULL); vkDestroyInstance(instance, NULL); SDL_DestroyWindow(window); @@ -264,6 +386,9 @@ bool is_device_ok(VkPhysicalDevice dev) { free(props); + VkPhysicalDeviceFeatures supported_features; + vkGetPhysicalDeviceFeatures(dev, &supported_features); + bool adequate_swapchain = false; if (swapchain) { struct swapchain_details details = query_swapchain(dev); @@ -271,7 +396,8 @@ bool is_device_ok(VkPhysicalDevice dev) { free_swapchain_details(details); } - return found_queue_indx(find_queue_families(dev)) && swapchain && adequate_swapchain; + return found_queue_indx(find_queue_families(dev)) && swapchain + && adequate_swapchain && supported_features.samplerAnisotropy; } void pick_physical_dev() { @@ -320,7 +446,9 @@ void create_logical_dev() { }, }; - VkPhysicalDeviceFeatures dev_features = { 0 }; + VkPhysicalDeviceFeatures dev_features = { + .samplerAnisotropy = VK_TRUE + }; const char * const ext = VK_KHR_SWAPCHAIN_EXTENSION_NAME; @@ -401,36 +529,44 @@ void create_swapchain() { } +VkImageView create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspect_flags) { + VkImageViewCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = aspect_flags, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + + VkImageView view; + + VkResult res = vkCreateImageView(gpu, &create_info, NULL, &view); + if (res != VK_SUCCESS) { + fputs("failed to create image view", stderr); + exit(-1); + } + + return view; +} + void create_image_views() { swapchain_image_views = malloc(sizeof(*swapchain_image_views) + sizeof(*swapchain_image_views->data) * swapchain_images->len); swapchain_image_views->len = swapchain_images->len; for (size_t i = 0; i < swapchain_images->len; i++) { - VkImageViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = swapchain_images->data[i], - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = swapchain_format, - .components = { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 - } - }; - - VkResult res = vkCreateImageView(gpu, &create_info, NULL, &swapchain_image_views->data[i]); - if (res != VK_SUCCESS) { - fputs("failed to create image view", stderr); - exit(-1); - } + swapchain_image_views->data[i] = create_image_view(swapchain_images->data[i], swapchain_format, VK_IMAGE_ASPECT_COLOR_BIT); } } @@ -447,7 +583,7 @@ struct code *readfile(const char *filename) { struct code *bytes = malloc(sizeof(*bytes) + sizeof(*bytes->data) * len); bytes->len = len; - fread(bytes->data, sizeof(*bytes->data), len, fp); + size_t read = fread(bytes->data, sizeof(*bytes->data), len, fp); fclose(fp); return bytes; @@ -493,8 +629,16 @@ void create_graphics_pipeline() { VkPipelineShaderStageCreateInfo shader_stages[] = { vert_shader_info, frag_shader_info }; + VkVertexInputBindingDescription desc = get_vertex_description(); + VkVertexInputAttributeDescription attrs[3]; + get_attribute_description(attrs); + VkPipelineVertexInputStateCreateInfo vertex_input_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &desc, + .vertexAttributeDescriptionCount = sizeof(attrs) / sizeof(*attrs), + .pVertexAttributeDescriptions = attrs }; VkPipelineInputAssemblyStateCreateInfo input_assembly = { @@ -541,7 +685,7 @@ void create_graphics_pipeline() { .polygonMode = VK_POLYGON_MODE_FILL, .lineWidth = 1.0f, .cullMode = VK_CULL_MODE_BACK_BIT, - .frontFace = VK_FRONT_FACE_CLOCKWISE, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = VK_FALSE, }; @@ -564,8 +708,21 @@ void create_graphics_pipeline() { .pAttachments = &color_state }; + VkPipelineDepthStencilStateCreateInfo depth_stencil = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS, + .depthBoundsTestEnable = VK_FALSE, + .minDepthBounds = 0.0f, + .maxDepthBounds = 1.0, + .stencilTestEnable = VK_FALSE, + }; + VkPipelineLayoutCreateInfo pipeline_layout_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &desc_layout }; VkResult res = vkCreatePipelineLayout(gpu, &pipeline_layout_create_info, NULL, &pipeline_layout); @@ -585,6 +742,7 @@ void create_graphics_pipeline() { .pMultisampleState = &multisampling, .pColorBlendState = &color_blending, .pDynamicState = &dynamic_state_info, + .pDepthStencilState = &depth_stencil, .layout = pipeline_layout, .renderPass = render_pass, .subpass = 0, @@ -602,6 +760,135 @@ void create_graphics_pipeline() { vkDestroyShaderModule(gpu, vert_shader, NULL); } +uint32_t find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags props) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(phy_gpu, &mem_props); + + for (size_t i = 0; i < mem_props.memoryTypeCount; i++) + if (type_filter & (1 << i) && (mem_props.memoryTypes[i].propertyFlags & props) == props) + return i; + + fputs("failed to find memory type", stderr); + exit(-1); +} + +void create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, + VkMemoryPropertyFlags props, VkBuffer *buffer, VkDeviceMemory *buffer_memory) { + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + + VkResult res = vkCreateBuffer(gpu, &buffer_info, NULL, buffer); + if (res != VK_SUCCESS) { + fputs("failed to create vertex buffer", stderr); + exit(-1); + } + + VkMemoryRequirements mem_reqs; + vkGetBufferMemoryRequirements(gpu, *buffer, &mem_reqs); + + VkMemoryAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = find_memory_type(mem_reqs.memoryTypeBits, props) + }; + + res = vkAllocateMemory(gpu, &alloc_info, NULL, buffer_memory); + if (res != VK_SUCCESS) { + fputs("failed to allocate memory", stderr); + exit(-1); + } + + vkBindBufferMemory(gpu, *buffer, *buffer_memory, 0); +} + +VkCommandBuffer being_single_command() { + VkCommandBufferAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandPool = command_pool, + .commandBufferCount = 1 + }; + + VkCommandBuffer cmd_buf; + vkAllocateCommandBuffers(gpu, &alloc_info, &cmd_buf); + + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + + vkBeginCommandBuffer(cmd_buf, &begin_info); + + return cmd_buf; +} + +void end_single_command(VkCommandBuffer command_buffer) { + vkEndCommandBuffer(command_buffer); + + VkSubmitInfo submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &command_buffer + }; + + vkQueueSubmit(gfx_queue, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(gfx_queue); + + vkFreeCommandBuffers(gpu, command_pool, 1, &command_buffer); +} + +void copy_buffer_to_image(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { + VkCommandBuffer command_buffer = being_single_command(); + + VkBufferImageCopy region = { + .bufferRowLength = 0, + .bufferOffset = 0, + .bufferImageHeight = 0, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { width, height, 1 } + }; + + vkCmdCopyBufferToImage(command_buffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + end_single_command(command_buffer); +} + +void copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) { + VkCommandBuffer cmd_buf = being_single_command(); + vkCmdCopyBuffer(cmd_buf, src, dst, 1, &(VkBufferCopy) { .size = size }); + end_single_command(cmd_buf); +} + +void create_vertex_buffer() { + VkBuffer tmp_buffer; + VkDeviceMemory tmp_mem; + create_buffer(sizeof(vertices), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem); + + void *data; + vkMapMemory(gpu, tmp_mem, 0, sizeof(vertices), 0, &data); + memcpy(data, vertices, sizeof(vertices)); + vkUnmapMemory(gpu, tmp_mem); + + create_buffer(sizeof(vertices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertex_buffer, &vertex_buffer_memory); + + copy_buffer(tmp_buffer, vertex_buffer, sizeof(vertices)); + + vkDestroyBuffer(gpu, tmp_buffer, NULL); + vkFreeMemory(gpu, tmp_mem, NULL); +} + void create_render_pass() { VkAttachmentDescription color_attach = { .format = swapchain_format, @@ -619,25 +906,44 @@ void create_render_pass() { .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; + VkAttachmentDescription depth_attach = { + .format = VK_FORMAT_D32_SFLOAT, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + }; + + VkAttachmentReference depth_attach_ref = { + .attachment = 1, + .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + }; + VkSubpassDescription subpass = { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, - .pColorAttachments = &color_attach_ref + .pColorAttachments = &color_attach_ref, + .pDepthStencilAttachment = &depth_attach_ref }; VkSubpassDependency dep = { .srcSubpass = VK_SUBPASS_EXTERNAL, .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }; + VkAttachmentDescription attachs[] = { color_attach, depth_attach }; + VkRenderPassCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = 1, - .pAttachments = &color_attach, + .attachmentCount = sizeof(attachs) / sizeof(*attachs), + .pAttachments = attachs, .subpassCount = 1, .pSubpasses = &subpass, .dependencyCount = 1, @@ -657,13 +963,14 @@ void create_framebuffers() { for (size_t i = 0; i < swapchain_image_views->len; i++) { VkImageView attachs[] = { - swapchain_image_views->data[i] + swapchain_image_views->data[i], + depth_image_view }; VkFramebufferCreateInfo framebuffer_info = { .sType =VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = render_pass, - .attachmentCount = 1, + .attachmentCount = sizeof(attachs) / sizeof(*attachs), .pAttachments = attachs, .width = swapchain_extent.width, .height = swapchain_extent.height, @@ -720,7 +1027,10 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) { exit(-1); } - VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + VkClearValue clear_color[] = { + { .color = {0.0f, 0.0f, 0.0f, 1.0f}}, + { .depthStencil = { 1.0f, 0 }} + }; VkRenderPassBeginInfo render_pass_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, @@ -729,8 +1039,8 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) { .extent = swapchain_extent, .offset = {0, 0} }, - .clearValueCount = 1, - .pClearValues = &clear_color + .clearValueCount = sizeof(clear_color) / sizeof(*clear_color), + .pClearValues = clear_color }; vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); @@ -752,7 +1062,16 @@ void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) { }; vkCmdSetScissor(buffer, 0, 1, &scissor); - vkCmdDraw(buffer, 3, 1, 0, 0); + + VkBuffer vertex_buffers[] = { vertex_buffer }; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(buffer, 0, 1, vertex_buffers, offsets); + vkCmdBindIndexBuffer(buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline_layout, 0, 1, &desc_sets[current_frame], 0, NULL); + + vkCmdDrawIndexed(buffer, sizeof(indices) / sizeof(*indices), 1, 0, 0, 0); vkCmdEndRenderPass(buffer); @@ -780,6 +1099,316 @@ void create_sync_objects() { } } +void create_index_buffer() { + VkBuffer tmp_buffer; + VkDeviceMemory tmp_mem; + create_buffer(sizeof(indices), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem); + + void *data; + vkMapMemory(gpu, tmp_mem, 0, sizeof(indices), 0, &data); + memcpy(data, indices, sizeof(indices)); + vkUnmapMemory(gpu, tmp_mem); + + create_buffer(sizeof(indices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &index_buffer, &index_buffer_memroy); + + copy_buffer(tmp_buffer, index_buffer, sizeof(indices)); + + vkDestroyBuffer(gpu, tmp_buffer, NULL); + vkFreeMemory(gpu, tmp_mem, NULL); +} + +void create_descriptor_layout() { + VkDescriptorSetLayoutBinding layout_bind[] = { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT + }, { + .binding = 1, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImmutableSamplers = NULL, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + } + }; + + + + VkDescriptorSetLayoutCreateInfo desc_create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = sizeof(layout_bind) / sizeof(*layout_bind), + .pBindings = layout_bind + }; + + VkResult res = vkCreateDescriptorSetLayout(gpu, &desc_create_info, NULL, &desc_layout); + if (res != VK_SUCCESS) { + fputs("failed to create descriptor set layout", stderr); + exit(-1); + } +} + +void create_uniform_buffers() { + for (size_t i = 0; i < MAX_FRAMES_INFLIGHT; i++) { + create_buffer(sizeof(struct ubo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniform_buffers[i], &uniform_memory[i]); + vkMapMemory(gpu, uniform_memory[i], 0, sizeof(struct ubo), 0, &mapped_buffers[i]); + } +} + +void create_descriptor_pool() { + VkDescriptorPoolSize pool_size[] = { + { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = MAX_FRAMES_INFLIGHT + }, { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = MAX_FRAMES_INFLIGHT + } + }; + + VkDescriptorPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .poolSizeCount = sizeof(pool_size) / sizeof(*pool_size), + .pPoolSizes = pool_size, + .maxSets = MAX_FRAMES_INFLIGHT + }; + + VkResult res = vkCreateDescriptorPool(gpu, &pool_info, NULL, &desc_pool); + if (res != VK_SUCCESS) { + fputs("failed to create descriptor pool", stderr); + exit(-1); + } +} + +void create_descriptor_sets() { + VkDescriptorSetLayout layouts[MAX_FRAMES_INFLIGHT] = { + desc_layout, + desc_layout + }; + + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = desc_pool, + .descriptorSetCount = MAX_FRAMES_INFLIGHT, + .pSetLayouts = layouts + }; + + VkResult res = vkAllocateDescriptorSets(gpu, &alloc_info, desc_sets); + if (res != VK_SUCCESS) { + fputs("failed to allocate descriptor sets", stderr); + exit(-1); + } + + for (size_t i = 0; i < MAX_FRAMES_INFLIGHT; i++) { + VkDescriptorBufferInfo buffer_info = { + .buffer = uniform_buffers[i], + .offset = 0, + .range = sizeof(struct ubo) + }; + + VkDescriptorImageInfo image_info = { + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = texture_view, + .sampler = texture_sampler + }; + + VkWriteDescriptorSet desc_write[] = { + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = desc_sets[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .pBufferInfo = &buffer_info, + }, { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = desc_sets[i], + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .pImageInfo = &image_info, + } + }; + + vkUpdateDescriptorSets(gpu, sizeof(desc_write) / sizeof(*desc_write), desc_write, 0, NULL); + } +} + +void create_image(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, VkMemoryPropertyFlags props, VkImage *img, VkDeviceMemory *memory) { + VkImageCreateInfo image_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .extent = { + .width = width, + .height = height, + .depth = 1 + }, + .mipLevels = 1, + .arrayLayers = 1, + .format = format, + .tiling = tiling, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .samples = VK_SAMPLE_COUNT_1_BIT + }; + + VkResult res = vkCreateImage(gpu, &image_info, NULL, img); + if (res != VK_SUCCESS) { + fputs("failed to create image", stderr); + exit(-1); + } + + VkMemoryRequirements mem_reqs; + vkGetImageMemoryRequirements(gpu, *img, &mem_reqs); + + VkMemoryAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = find_memory_type(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + }; + + res = vkAllocateMemory(gpu, &alloc_info, NULL, memory); + if (res != VK_SUCCESS) { + fputs("failed to allocate memory", stderr); + exit(-1); + } + + vkBindImageMemory(gpu, *img, *memory, 0); +} + +void transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) { + VkCommandBuffer command_buffer = being_single_command(); + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .oldLayout = old_layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .srcAccessMask = 0, + .dstAccessMask = 0 + }; + + VkPipelineStageFlags src_flags, dst_flags; + + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + src_flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dst_flags = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + src_flags = VK_PIPELINE_STAGE_TRANSFER_BIT; + dst_flags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + fputs("unsupported layout transition", stderr); + exit(-1); + } + + vkCmdPipelineBarrier(command_buffer, src_flags, dst_flags, 0, 0, NULL, 0, NULL, 1, &barrier); + + end_single_command(command_buffer); +} + +void create_texture_image() { + int32_t width, height, channels; + + stbi_uc *pixels = stbi_load("pfp.png", &width, &height, &channels, STBI_rgb_alpha); + if (!pixels) { + fputs("failed to open pfp.png", stderr); + exit(-1); + } + + VkDeviceSize image_size = width * height * 4; + + VkBuffer tmp_buffer; + VkDeviceMemory tmp_mem; + + create_buffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &tmp_buffer, &tmp_mem); + + void *data; + vkMapMemory(gpu, tmp_mem, 0, image_size, 0, &data); + memcpy(data, pixels, image_size); + vkUnmapMemory(gpu, tmp_mem); + + stbi_image_free(pixels); + + create_image(width, height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture_image, &texture_image_memory); + + transition_image_layout(texture_image, VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + copy_buffer_to_image(tmp_buffer, texture_image, width, height); + + transition_image_layout(texture_image, VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(gpu, tmp_buffer, NULL); + vkFreeMemory(gpu, tmp_mem, NULL); +} + +void create_texture_view() { + texture_view = create_image_view(texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); +} + +void create_texture_sampler() { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(phy_gpu, &props); + VkSamplerCreateInfo sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .anisotropyEnable = VK_TRUE, + .maxAnisotropy = props.limits.maxSamplerAnisotropy, + .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .mipLodBias = 0.0f, + .minLod = 0.0f, + .maxLod = 0.0f + }; + + VkResult res = vkCreateSampler(gpu, &sampler_info, NULL, &texture_sampler); + if (res != VK_SUCCESS) { + fputs("failed to create image sampler", stderr); + exit(-1); + } +} + +void create_depth_resources() { + VkFormat depth_format = VK_FORMAT_D32_SFLOAT; + create_image(swapchain_extent.width, swapchain_extent.height, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &depth_image, &depth_memory); + + depth_image_view = create_image_view(depth_image, VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); +} + void recreate_swapchain() { int32_t width = 0, height = 0; SDL_GetWindowSize(window, &width, &height); @@ -795,6 +1424,7 @@ void recreate_swapchain() { create_swapchain(); create_image_views(); + create_depth_resources(); create_framebuffers(); } @@ -831,13 +1461,41 @@ void init() { create_swapchain(); create_image_views(); create_render_pass(); + create_descriptor_layout(); create_graphics_pipeline(); - create_framebuffers(); create_command_pool(); + create_depth_resources(); + create_framebuffers(); + create_texture_image(); + create_texture_view(); + create_texture_sampler(); + create_vertex_buffer(); + create_index_buffer(); + create_uniform_buffers(); + create_descriptor_pool(); + create_descriptor_sets(); create_command_buffers(); create_sync_objects(); } +void update_uniform_buffer(uint32_t current_image) { + static double rotate = 0.1; + struct ubo ubo = {0}; + mat4 id; + mat4x4_identity(id); + mat4x4_rotate(ubo.model, id, 0, 0, 1.0f, rotate * 90); + mat4x4_translate_in_place(ubo.model, 0, 0, 1); + + mat4x4_look_at(ubo.view, (vec3){ 2, 2, 2 }, (vec3){ 0, 0, 1}, (vec3){ 0, 0, 1 }); + + mat4x4_perspective(ubo.proj, 45, swapchain_extent.width / (float) swapchain_extent.height, 0.1f, 10.0f); + + ubo.proj[1][1] *= -1; + + memcpy(mapped_buffers[current_frame], &ubo, sizeof(ubo)); + rotate += 0.0001; +} + void draw() { vkWaitForFences(gpu, 1, &in_flight_fence[current_frame], VK_TRUE, UINT64_MAX); @@ -845,9 +1503,9 @@ void draw() { VkResult res = vkAcquireNextImageKHR(gpu, swapchain, UINT64_MAX, image_avail[current_frame], VK_NULL_HANDLE, &image_index); switch (res) { case VK_SUCCESS: + case VK_SUBOPTIMAL_KHR: break; case VK_ERROR_OUT_OF_DATE_KHR: - case VK_SUBOPTIMAL_KHR: recreate_swapchain(); return; default: @@ -873,6 +1531,8 @@ void draw() { .pCommandBuffers = &command_buffers[current_frame] }; + update_uniform_buffer(current_frame); + res = vkQueueSubmit(gfx_queue, 1, &submit_info, in_flight_fence[current_frame]); if (res != VK_SUCCESS) { fputs("failed to submit draw command buffer", stderr); @@ -889,7 +1549,18 @@ void draw() { .pImageIndices = &image_index }; - vkQueuePresentKHR(present_queue, &present_info); + res = vkQueuePresentKHR(present_queue, &present_info); + switch (res) { + case VK_SUCCESS: + break; + case VK_SUBOPTIMAL_KHR: + case VK_ERROR_OUT_OF_DATE_KHR: + recreate_swapchain(); + return; + default: + fputs("failed to acquire swapchain images", stderr); + exit(-1); + } current_frame = (current_frame + 1) % MAX_FRAMES_INFLIGHT; } diff --git a/pfp.png b/pfp.png new file mode 100644 index 0000000..622f780 Binary files /dev/null and b/pfp.png differ diff --git a/shader.frag b/shader.frag index c9e559f..2209f4e 100644 --- a/shader.frag +++ b/shader.frag @@ -1,8 +1,10 @@ #version 450 +layout(binding = 1) uniform sampler2D texture_sampler; layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 frag_text_coords; layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0); + outColor = vec4(fragColor * texture(texture_sampler, frag_text_coords).rgb, 1.0); } diff --git a/shader.vert b/shader.vert index 9e69113..7535193 100644 --- a/shader.vert +++ b/shader.vert @@ -1,20 +1,20 @@ #version 450 -layout(location = 0) out vec3 fragColor; +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); +layout(location = 0) in vec3 in_position; +layout(location = 1) in vec3 in_color; +layout(location = 2) in vec2 in_text_coords; -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); +layout(location = 0) out vec3 frag_color; +layout(location = 1) out vec2 frag_text_coords; void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(in_position, 1.0); + frag_color = in_color; + frag_text_coords = in_text_coords; } -- cgit v1.2.3