working triangles + uniform transfer

This commit is contained in:
Андреев Григорий 2025-06-08 04:55:08 +03:00
commit e05eb37c79
18 changed files with 2884 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.spv
cmake-build-debug/
.idea/
vgcore.*
gen/

21
CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.30)
project(splitter_draft C)
#include_directories(${CMAKE_SOURCE_DIR})
set(CMAKE_C_FLAGS "-Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type --std=c99 -g -ggdb -O0")
add_compile_definitions(_POSIX_C_SOURCE=200112L )
add_executable(main src/l1/main.c)
add_executable(0_test src/l1/tests/t0.c)
add_executable(1_test src/l1/tests/t1.c)
add_executable(0_render_test src/l2/tests/r0.c)
target_link_libraries(0_render_test -lvulkan -lX11 -lm)
# Recursively collect all .h files in the src directory.
file(GLOB_RECURSE HEADER_FILES "${CMAKE_SOURCE_DIR}/src/*.h")
# Do not build utku
add_executable(utka src/l1/main.c ${HEADER_FILES})

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
HEADERS := $(shell find src -type f -name '*.h')
all: prototype1
prototype1: src/l1/main.c $(HEADERS)
@gcc --std c99 -o $@ src/l1/main.c
clean:
@rm -f prototype1
.PHONY: all clean

View File

@ -0,0 +1,9 @@
#ifndef PROTOTYPE1_SRC_L1_CORE_OPTION_INT_PRIMITIVES_H
#define PROTOTYPE1_SRC_L1_CORE_OPTION_INT_PRIMITIVES_H
#include "util.h"
OptionT_struct_Definition(U32)
OptionT_method_Definition(U32)
#endif

View File

@ -0,0 +1,13 @@
#ifndef PROTOTYPE1_SRC_CORE_VECSPAN_VECSPAN_INT_PRIMITIVES_H
#define PROTOTYPE1_SRC_CORE_VECSPAN_VECSPAN_INT_PRIMITIVES_H
#include "VecSpan_int_primitives.h"
SpanT_VecT_trivmove_COMPLETE_Definition(VecU8)
VecT_with_default_method_Definition(VecU8)
SpanT_VecT_trivmove_COMPLETE_Definition(VecU32)
VecT_with_default_method_Definition(VecU32)
SpanT_VecT_trivmove_COMPLETE_Definition(VecU64)
VecT_with_default_method_Definition(VecU64)
#endif

View File

@ -0,0 +1,38 @@
#ifndef PROTOTYPE1_SRC_CORE_VECU8_H
#define PROTOTYPE1_SRC_CORE_VECU8_H
#include "util.h"
SpanT_VecT_trivmove_COMPLETE_Definition(U8)
VecT_primitive_zeroinit_method_Definition(U8)
SpanT_comparable_method_Definition(U8)
VecU8 VecU8_from_cstr(const char* dc) {
size_t n = strlen(dc);
VecU8 res = (VecU8){ .buf = safe_calloc(n, 1), .len = n, .capacity = n };
memcpy(res.buf, dc, n);
return res;
}
#define vcstr(dc) VecU8_from_cstr(dc)
ConstSpanU8 ConstSpanU8_from_cstr(const char* dc) {
return (ConstSpanU8){.data = (U8*)dc, .len = strlen(dc)};
}
#define cstr(dc) ConstSpanU8_from_cstr(dc)
void ConstSpanU8_print(ConstSpanU8 str) {
for (size_t i = 0; i < str.len; i++)
putchar((int)*ConstSpanU8_at(str, i));
}
SpanT_VecT_trivmove_COMPLETE_Definition(U32)
VecT_primitive_zeroinit_method_Definition(U32)
SpanT_comparable_method_Definition(U32)
SpanT_VecT_trivmove_COMPLETE_Definition(U64)
VecT_primitive_zeroinit_method_Definition(U64)
SpanT_comparable_method_Definition(U64)
#endif

View File

@ -0,0 +1,61 @@
#ifndef PROTOTYPE1_SRC_CORE_INT_PRIMITIVES_H
#define PROTOTYPE1_SRC_CORE_INT_PRIMITIVES_H
#include <stdint.h>
// *Crosses-fingers* Please optimize this all out, please optimize this all out
typedef uint8_t U8;
typedef uint16_t U16;
typedef uint32_t U32;
typedef uint64_t U64;
typedef int8_t S8;
typedef int16_t S16;
typedef int32_t S32;
typedef int64_t S64;
#define U8_drop(x) {}
#define U16_drop(x) {}
#define U32_drop(x) {}
#define U64_drop(x) {}
#define S8_drop(x) {}
#define S16_drop(x) {}
#define S32_drop(x) {}
#define S64_drop(x) {}
#define U8_clone(vp) (*(vp))
#define U16_clone(vp) (*(vp))
#define U32_clone(vp) (*(vp))
#define U64_clone(vp) (*(vp))
#define S8_clone(vp) (*(vp))
#define S16_clone(vp) (*(vp))
#define S32_clone(vp) (*(vp))
#define S64_clone(vp) (*(vp))
#define U8_equal_U8(ap, bp) (*(ap) == *(bp))
#define U16_equal_U16(ap, bp) (*(ap) == *(bp))
#define U32_equal_U32(ap, bp) (*(ap) == *(bp))
#define U64_equal_U64(ap, bp) (*(ap) == *(bp))
#define S8_equal_S8(ap, bp) (*(ap) == *(bp))
#define S16_equal_S16(ap, bp) (*(ap) == *(bp))
#define S32_equal_S32(ap, bp) (*(ap) == *(bp))
#define S64_equal_S64(ap, bp) (*(ap) == *(bp))
#define U8_less_U8(ap, bp) (*(ap) < *(bp))
#define U16_less_U16(ap, bp) (*(ap) < *(bp))
#define U32_less_U32(ap, bp) (*(ap) < *(bp))
#define U64_less_U64(ap, bp) (*(ap) < *(bp))
#define S8_less_S8(ap, bp) (*(ap) < *(bp))
#define S16_less_S16(ap, bp) (*(ap) < *(bp))
#define S32_less_S32(ap, bp) (*(ap) < *(bp))
#define S64_less_S64(ap, bp) (*(ap) < *(bp))
#define int_minmax_function_Definition(T) \
T MIN_##T (T a, T b){ return a < b ? a : b; } \
T MAX_##T (T a, T b){ return a < b ? b : a; }
int_minmax_function_Definition(U8)
int_minmax_function_Definition(U32)
int_minmax_function_Definition(U64)
#endif

312
src/l1/core/util.h Normal file
View File

@ -0,0 +1,312 @@
#ifndef PROTOTYPE1_SRC_CORE_UTIL_H
#define PROTOTYPE1_SRC_CORE_UTIL_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
#include <inttypes.h>
#include "int_primitives.h"
// Gonna change it when I face pedantic compilers
#define NORETURN [[noreturn]]
#define NODISCARD [[nodiscard]]
typedef enum {
Option_Some, Option_None
} Option_variant;
typedef enum {
Result_Ok, Result_Err
} Result_variant;
NORETURN
void abortf(const char* format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
abort();
}
void* safe_malloc(size_t n) {
void* res = malloc(n);
if (!res)
abortf("allocation failure");
return res;
}
void* safe_calloc(size_t nmemb, size_t size) {
void* res = calloc(nmemb, size);
if (!res)
abortf("allocation failure");
return res;
}
void* safe_realloc(void* ptr, size_t n) {
void* res = realloc(ptr, n);
if (!res && n)
abortf("allocation failure");
return res;
}
#define unsigned_safe_mul_Definition(TSZ) \
U##TSZ safe_mul_U##TSZ(U##TSZ a, U##TSZ b) { \
if (b > 0 && a > UINT##TSZ##_MAX / b) \
abortf("Overflow in multiplication: %" PRIu##TSZ " * %" PRIu##TSZ "\n", a, b); \
return a * b; \
}
unsigned_safe_mul_Definition(64)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// I assume that new_length > old_capacity
size_t Vec_get_new_capacity(size_t old_capacity, size_t new_length) {
if (!old_capacity)
return new_length;
while (old_capacity < new_length)
old_capacity *= 2;
return old_capacity;
}
#define VecT_trivmove_struct_Definition_custom_name(T, VecT) \
typedef struct {T* buf; size_t len; size_t capacity;} VecT;
#define VecT_trivmove_method_Definition_custom_name(T, VecT) \
NODISCARD VecT VecT##_new() { \
return (VecT){NULL, 0, 0}; \
} \
void VecT##_drop(VecT obj) { \
for (size_t i = 0; i < obj.len; i++) \
T ## _drop(obj.buf[i]); \
free(obj.buf); \
} \
NODISCARD VecT VecT##_new_filled(size_t len, const T* el) { \
VecT res = (VecT){.buf = safe_calloc(len, sizeof(T)), .len = len, .capacity = len}; \
for (size_t i = 0; i < len; i++) \
res.buf[i] = T##_clone(el); \
return res; \
} \
void VecT##_append(VecT* self, T el) { \
size_t new_length = self->len + 1; \
if (new_length > self->capacity) { \
size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length); \
self->buf = safe_realloc(self->buf, new_capacity * sizeof(T)); \
self->capacity = new_capacity; \
} \
self->buf[self->len] = el; \
self->len = new_length; \
} \
NODISCARD T VecT##_pop(VecT* self) { \
assert(self->len > 0); \
self->len--; \
return self->buf[self->len]; \
} \
void VecT##_pop_and_drop(VecT* self) { \
T##_drop(VecT##_pop(self)); \
} \
NODISCARD VecT VecT##_swap_with_empty(VecT* cell) { \
VecT val = *cell; *cell = (VecT){NULL, 0, 0}; return val; \
} \
T* VecT##_at(VecT* self, size_t i) { \
assert(i < self->len); \
return self->buf + i; \
} \
const T* VecT##_cat(const VecT* self, size_t i) { \
assert(i < self->len); \
return self->buf + i; \
} \
NODISCARD VecT VecT##_clone(const VecT* self){ \
VecT res = (VecT){.buf = safe_calloc(self->len, sizeof(T)), .len = self->len, .capacity = self->len}; \
for (size_t i = 0; i < self->len; i++) \
res.buf[i] = T##_clone(&self->buf[i]); \
return res; \
} \
void VecT##_append_vec(VecT* self, VecT b) { \
size_t new_length = self->len + b.len; \
if (new_length > self->capacity) { \
size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length); \
self->buf = safe_realloc(self->buf, new_capacity * sizeof(T)); \
self->capacity = new_capacity; \
} \
for (size_t i = 0; i < b.len; i++){ \
self->buf[self->len + i] = b.buf[i]; \
} \
self->len = new_length; \
free(b.buf); \
}
#define VecT_trivmove_equal_method_Definition_custom_name(T, VecT) \
bool VecT##_equal_##VecT(const VecT* A, const VecT* B) { \
if (A->len != B->len) \
return false; \
for (size_t i = 0; i < A->len; i++) { \
if (!T##_equal_##T(A->buf + i, B->buf + i)) \
return false; \
} \
return true; \
}
#define VecT_primitive_zeroinit_method_Definition_custom_name(T, VecT) \
VecT VecT##_new_zeroinit(size_t len) { \
return (VecT){.buf = safe_calloc(len, sizeof(T)), .len = len, .capacity = len}; \
}
#define VecT_with_default_method_Definition_custom_name(T, VecT) \
VecT VecT##_new_of_size(size_t len) { \
VecT res = (VecT){.buf = safe_calloc(len, sizeof(T)), .len = len, .capacity = len}; \
for (size_t i = 0; i < len; i++) \
res.buf[i] = T##_new(); \
return res; \
}
#define VecT_trivmove_struct_Definition(T) VecT_trivmove_struct_Definition_custom_name(T, Vec##T)
#define VecT_trivmove_method_Definition(T) VecT_trivmove_method_Definition_custom_name(T, Vec##T)
#define VecT_trivmove_equal_method_Definition(T) VecT_trivmove_equal_method_Definition_custom_name(T, Vec##T)
#define VecT_primitive_zeroinit_method_Definition(T) VecT_primitive_zeroinit_method_Definition_custom_name(T, Vec##T)
#define VecT_with_default_method_Definition(T) VecT_with_default_method_Definition_custom_name(T, Vec##T)
#define SpanT_struct_Definition_custom_name(T, SpanT, ConstSpanT) \
typedef struct {const T* data; size_t len;} ConstSpanT; \
typedef struct {T* data; size_t len;} SpanT; \
// todo: rename span to MutSpan and ConstSpan to Span
#define SpanT_method_Definition_custom_name(T, SpanT, ConstSpanT) \
void ConstSpanT##_drop(ConstSpanT) {} \
void SpanT##_drop(SpanT) {} \
ConstSpanT ConstSpanT##_clone(ConstSpanT self) { \
return (ConstSpanT){.data = self.data, .len = self.len}; \
} \
SpanT SpanT##_clone(SpanT self) { \
return (SpanT){.data = self.data, .len = self.len}; \
} \
bool ConstSpanT##_equal_##ConstSpanT(ConstSpanT a, ConstSpanT b) { \
return a.data == b.data && a.len == b.len; \
} \
bool SpanT##_equal_##SpanT(SpanT a, SpanT b) { \
return a.data == b.data && a.len == b.len; \
} \
ConstSpanT SpanT##_to_##ConstSpanT(SpanT self) { \
return (ConstSpanT){.data = self.data, .len = self.len}; \
} \
SpanT SpanT##_span(SpanT span, size_t start, size_t len){ \
assert(start < SIZE_MAX - len && start + len <= span.len); \
return (SpanT){.data = span.data + start, .len = len}; \
}; \
ConstSpanT ConstSpanT##_cspan(ConstSpanT span, size_t start, size_t len){ \
assert(start < SIZE_MAX - len && start + len <= span.len); \
return (ConstSpanT){.data = span.data + start, .len = len}; \
}; \
T* SpanT##_at(SpanT self, size_t i) { \
assert(i < self.len); \
return self.data + i; \
} \
const T* SpanT##_cat(const SpanT self, size_t i) { \
assert(i < self.len); \
return self.data + i; \
} \
const T* ConstSpanT##_at(ConstSpanT self, size_t i) { \
assert(i < self.len); \
return self.data + i; \
}
#define SpanT_VecT_method_Definition_custom_name(T, SpanT, ConstSpanT, VecT) \
NODISCARD VecT VecT##_from_##span(ConstSpanT src){ \
VecT res = (VecT){ .buf = safe_calloc(src.len, sizeof(T)), .len = src.len, .capacity = src.len }; \
for (size_t i = 0; i < src.len; i++) \
res.buf[i] = T##_clone(&src.data[i]); \
return res; \
} \
ConstSpanT VecT##_to_##ConstSpanT(const VecT* vec){ \
return (ConstSpanT){vec->buf, vec->len}; \
} \
SpanT VecT##_to_##SpanT(VecT* vec){ \
return (SpanT){vec->buf, vec->len}; \
} \
SpanT VecT##_span(VecT* vec, size_t start, size_t len){ \
assert(start < SIZE_MAX - len && start + len <= vec->len); \
return (SpanT){.data = vec->buf + start, .len = len}; \
} \
ConstSpanT VecT##_cspan(const VecT* vec, size_t start, size_t len){ \
assert(start < SIZE_MAX - len && start + len <= vec->len); \
return (ConstSpanT){.data = vec->buf + start, .len = len}; \
} \
void VecT##_append_span(VecT* self, ConstSpanT b) { \
size_t new_length = self->len + b.len; \
if (new_length > self->capacity) { \
size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length); \
self->buf = safe_realloc(self->buf, new_capacity * sizeof(T)); \
self->capacity = new_capacity; \
} \
for (size_t i = 0; i < b.len; i++){ \
self->buf[self->len + i] = T##_clone(&b.data[i]); \
} \
self->len = new_length; \
} \
// Requires normal less method and adds qcompare method for T to use in qsort
#define SpanT_comparable_method_Definition_custom_name(T, SpanT, ConstSpanT) \
int T##_qcompare(const void* a, const void* b) { \
const T* A = (const T*)a; \
const T* B = (const T*)b; \
return (int)T##_less_##T(B, A) - (int)T##_less_##T(A, B); \
} \
void SpanT##_sort(SpanT self) { \
qsort(self.data, self.len, sizeof(T), T##_qcompare); \
}
#define SpanT_struct_Definition(T) SpanT_struct_Definition_custom_name(T, Span##T, ConstSpan##T)
#define SpanT_method_Definition(T) SpanT_method_Definition_custom_name(T, Span##T, ConstSpan##T)
#define SpanT_VecT_method_Definition(T) SpanT_VecT_method_Definition_custom_name(T, Span##T, ConstSpan##T, Vec##T)
#define SpanT_comparable_method_Definition(T) SpanT_comparable_method_Definition_custom_name(T, Span##T, ConstSpan##T)
#define SpanT_VecT_trivmove_COMPLETE_Definition(T) \
VecT_trivmove_struct_Definition(T) VecT_trivmove_method_Definition(T) \
SpanT_struct_Definition(T) SpanT_method_Definition(T) SpanT_VecT_method_Definition(T)
#define OptionT_struct_Definition_custom_name(T, OptionT) \
typedef struct { Option_variant variant; T some; } OptionT;
#define OptionT_method_Definition_custom_name(T, OptionT) \
OptionT None_##T(){\
return (OptionT){ .variant = Option_None }; \
}; \
OptionT Some_##T(T obj){ \
return (OptionT){ .variant = Option_Some, .some = obj }; \
} \
bool OptionT##_is_some(const OptionT* self) { \
return self->variant == Option_Some; \
}\
bool OptionT##_is_none(const OptionT* self) { \
return self->variant == Option_None; \
}\
const T* OptionT##_expect_const_ptr(const OptionT* self){ \
if (self->variant == Option_None) \
abortf("Expected something in const " #OptionT "* got None\n"); \
return &self->some; \
} \
T* OptionT##_expect_ptr(OptionT* self){ \
if (self->variant == Option_None) \
abortf("Expected something in " #OptionT "* got None\n"); \
return &self->some; \
} \
T OptionT##_expect(OptionT self){ \
if (self.variant == Option_None) \
abortf("Expected something in " #OptionT " got None\n"); \
return self.some; \
}
// todo: add clone and drop methods
#define OptionT_struct_Definition(T) OptionT_struct_Definition_custom_name(T, Option##T)
#define OptionT_method_Definition(T) OptionT_method_Definition_custom_name(T, Option##T)
#endif

76
src/l1/main.c Normal file
View File

@ -0,0 +1,76 @@
#include <stdio.h>
#include "system/fileio.h"
NODISCARD VecU8 begin_header(ConstSpanU8 guard) {
VecU8 res = VecU8_new();
VecU8_append_span(&res, cstr("#ifndef "));
VecU8_append_span(&res, guard);
VecU8_append_span(&res, cstr("\n#define "));
VecU8_append_span(&res, guard);
VecU8_append_span(&res, cstr("\n/* Automatically generated file. Do not edit it. */\n"));
return res;
}
void finish_header(VecU8 text_before_endif) {
VecU8_append_span(&text_before_endif, cstr("#endif\n"));
write_whole_file_or_abort("geom.h", VecU8_to_ConstSpanU8(&text_before_endif));
VecU8_drop(text_before_endif);
}
void string_append_spaces(VecU8* str, int sc) {
for (int i = 0; i < sc; i++)
VecU8_append(str, ' ');
}
NODISCARD VecU8 generate_xvecy_struct_definition(ConstSpanU8 xvec, ConstSpanU8 member, int cc) {
assert(2 <= cc && cc <= 4);
VecU8 res = VecU8_new();
VecU8_append_span(&res, cstr("typedef struct {\n"));
string_append_spaces(&res, 4);
VecU8_append_span(&res, member);
VecU8_append_span(&res, cstr(" x;\n"));
string_append_spaces(&res, 4);
VecU8_append_span(&res, member);
VecU8_append_span(&res, cstr(" y;\n"));
if (cc >= 3) {
string_append_spaces(&res, 4);
VecU8_append_span(&res, member);
VecU8_append_span(&res, cstr(" z;\n"));
}
if (cc >= 4) {
string_append_spaces(&res, 4);
VecU8_append_span(&res, member);
VecU8_append_span(&res, cstr(" w;\n"));
}
VecU8_append_span(&res, cstr("} "));
VecU8_append_span(&res, xvec);
VecU8_append(&res, '0' + cc);
VecU8_append_span(&res, cstr(";\n\n"));
return res;
}
NODISCARD VecU8 generate_xvec234_struct_definition(ConstSpanU8 xvec, ConstSpanU8 member) {
VecU8 res = VecU8_new();
VecU8_append_vec(&res, generate_xvecy_struct_definition(xvec, member, 2));
VecU8_append_vec(&res, generate_xvecy_struct_definition(xvec, member, 3));
VecU8_append_vec(&res, generate_xvecy_struct_definition(xvec, member, 4));
return res;
}
void generate_geometry_header() {
VecU8 res = begin_header(cstr("PROTOTYPE1_GEN_GEOM"));
VecU8_append_vec(&res, generate_xvec234_struct_definition(cstr("ivec"), cstr("int32_t")));
VecU8_append_vec(&res, generate_xvec234_struct_definition(cstr("uvec"), cstr("uint32_t")));
VecU8_append_vec(&res, generate_xvec234_struct_definition(cstr("vec"), cstr("float")));
VecU8_append_vec(&res, generate_xvec234_struct_definition(cstr("dvec"), cstr("double")));
finish_header(res);
}
int main() {
generate_geometry_header();
}

67
src/l1/system/fileio.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef PROTOTYPE1_SRC_SYSTEM_FILEIO_H
#define PROTOTYPE1_SRC_SYSTEM_FILEIO_H
#include "../core/VecSpan_int_primitives.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
typedef struct {
Result_variant variant;
union {
VecU8 Ok;
int Err;
};
} Result_VecU8_or_int;
void Result_VecU8_or_int_drop(Result_VecU8_or_int obj) {
if (obj.variant == Result_Ok)
VecU8_drop(obj.Ok);
}
typedef struct {
Result_variant variant;
int Err;
} Result_ok_or_int;
void Result_ok_or_int_drop(Result_ok_or_int obj) {}
NODISCARD VecU8 read_whole_file_or_abort(const char* filename) {
FILE* fp = fopen(filename, "rb");
if (!fp) {
abortf("Can't open file %s: %s\n", filename, strerror(errno));
}
if (fseek(fp, 0, SEEK_END) != 0) {
abortf("fseek: %s\n", strerror(errno));
}
long file_size = ftell(fp);
if (file_size < 0) {
abortf("ftell: %s\n", strerror(errno));
}
if (fseek(fp, 0, SEEK_SET) != 0) {
abortf("fseek: %s\n", strerror(errno));
}
VecU8 result = VecU8_new_zeroinit(file_size);
size_t nread = fread(result.buf, 1, (size_t)file_size, fp);
if (nread < file_size) {
abortf("fread\n");
}
fclose(fp);
return result;
}
void write_whole_file_or_abort(const char* filename, ConstSpanU8 content) {
FILE* fd = fopen(filename, "wb");
if (!fd) {
abortf("Can't open file %s: %s\n", filename, strerror(errno));
}
if (fwrite(content.data, 1, content.len, fd) < content.len) {
abortf("fwrite\n");
}
fclose(fd);
}
#endif

87
src/l1/tests/t0.c Normal file
View File

@ -0,0 +1,87 @@
#include "../core/VecSpan_int_primitives.h"
int main() {
VecU8 a = VecU8_new();
VecU8_append(&a, 116);
assert(a.len == 1);
assert(a.capacity == 1);
VecU8_append(&a, 18);
assert(a.len == 2);
assert(a.capacity == 2);
VecU8_append(&a, 180);
assert(a.len == 3);
assert(a.capacity == 4);
VecU8_append(&a, 9);
assert(a.len == 4);
assert(a.capacity == 4);
VecU8_append(&a, 90);
assert(a.len == 5);
assert(a.capacity == 8);
assert(*VecU8_cat(&a, 0) == 116);
assert(*VecU8_at(&a, 1) == 18);
assert(*VecU8_cat(&a, 2) == 180);
assert(*VecU8_at(&a, 3) == 9);
assert(*VecU8_cat(&a, 4) == 90);
VecU8_drop(a);
U32 five = 5;
VecU32 b = VecU32_new_filled(3, &five);
assert(b.len == 3);
assert(b.capacity == 3);
VecU32_append(&b, 41);
assert(b.len == 4);
assert(b.capacity == 6);
VecU32 c = VecU32_new();
VecU32_append(&c, 5);
VecU32_append(&c, 5);
VecU32_append(&c, 5);
VecU32_append(&c, 41);
assert(VecU32_equal_VecU32(&b, &c));
VecU32_append(&c, 7);
assert(!VecU32_equal_VecU32(&b, &c));
U32 x = VecU32_pop(&c);
assert(x == 7);
assert(VecU32_equal_VecU32(&b, &c));
// Now b = c = [5, 5, 5, 7]
for (int i = 0; i < 4; i++) {
printf("%u %u\n", *VecU32_at(&b, i), *VecU32_at(&c, i));
}
*VecU32_at(&b, 3) = 17;
assert(!VecU32_equal_VecU32(&b, &c));
*VecU32_at(&c, 3) = 17;
assert(VecU32_equal_VecU32(&b, &c));
SpanU32 s0 = VecU32_span(&c, 4, 0);
SpanU32 s1 = VecU32_span(&c, 3, 1);
SpanU32 s2 = VecU32_span(&c, 2, 2);
SpanU32 s3 = VecU32_span(&c, 1, 3);
SpanU32 s4 = VecU32_span(&c, 0, 4);
assert(*SpanU32_cat(s4, 3) == 17);
for (size_t i = 0; i < 3; i++) {
*VecU32_at(&c, i) = i;
}
*SpanU32_at(s3, 2) = 3;
assert(*SpanU32_at(s1, 0) == 3);
assert(*SpanU32_at(s2, 0) == 2);
assert(*SpanU32_at(s3, 0) == 1);
assert(*SpanU32_at(s4, 0) == 0);
VecU32 d = VecU32_clone(&c);
VecU32 e = VecU32_clone(&c);
VecU32_append(&c, 50);
VecU32_append(&e, 500);
assert(d.len == 4);
assert(d.capacity == 4);
assert(c.len == 5);
for (int i = 0; i < 4; i++) {
VecU32_pop_and_drop(&d);
}
assert(*VecU32_cat(&e, 4) == 500);
VecU32_drop(e);
VecU32_drop(d);
VecU32_drop(c);
VecU32_drop(b);
return 0;
}

36
src/l1/tests/t1.c Normal file
View File

@ -0,0 +1,36 @@
#include "../core/VecSpan_Vec_int_primitives.h"
int main() {
VecU64 v = VecU64_new();
for (int i = 0; i < 3; i++) {
VecU64_append(&v, i);
*VecU64_at(&v, i) = *VecU64_cat(&v, i) + 10;
}
VecVecU64 a = VecVecU64_new_filled(2, &v);
VecU64_drop(v);
assert(VecU64_equal_VecU64(VecVecU64_at(&a, 0), VecVecU64_at(&a, 1)));
VecU64_pop_and_drop(VecVecU64_at(&a, 1));
assert(!VecU64_equal_VecU64(VecVecU64_at(&a, 0), VecVecU64_at(&a, 1)));
VecVecU64 b = VecVecU64_new();
VecVecU64_append(&b, VecU64_new());
assert(!VecVecU64_equal_VecVecU64(&a, &b));
VecVecU64_append(&b, VecU64_new());
assert(!VecVecU64_equal_VecVecU64(&a, &b));
for (int i = 0; i < 3; i++) {
VecU64_append(VecVecU64_at(&b, 0), 10 + i);
}
assert(!VecVecU64_equal_VecVecU64(&a, &b));
for (int i = 0; i < 2; i++) {
VecU64_append(VecVecU64_at(&b, 1), 10 + i);
}
assert(VecVecU64_equal_VecVecU64(&a, &b));
VecVecU64 c = VecVecU64_clone(&a);
VecVecU64 d = VecVecU64_clone(&b);
VecVecU64_pop_and_drop(&a);
assert(VecVecU64_equal_VecVecU64(&c, &b));
assert(VecVecU64_equal_VecVecU64(&c, &d));
VecVecU64_drop(d);
VecVecU64_drop(c);
VecVecU64_drop(b);
VecVecU64_drop(a);
}

1365
src/l2/margaret/margaret.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
#ifndef PROTOTYPE1_SRC_L2_MARGARET_STRINGOP_H
#define PROTOTYPE1_SRC_L2_MARGARET_STRINGOP_H
#include "../../l1/core/VecSpan_int_primitives.h"
U8 U8_to_lowercase(U8 ch) {
if ('A' <= ch && ch <= 'Z')
return ch - 'A' + 'a';
return ch;
}
void string_to_lowercase(VecU8* str) {
for (size_t i = 0; i < str->len; i++) {
*VecU8_at(str, i) = U8_to_lowercase(*VecU8_cat(str, i));
}
}
// Worst case time complexity: O(nm)
bool string_contains_string_ignorecase(VecU8 str1, VecU8 str2) {
string_to_lowercase(&str1);
string_to_lowercase(&str2);
size_t L = str2.len;
for (size_t i = 0; i + L <= str1.len; i++) {
for (size_t j = 0; j < L; j++) {
if (*VecU8_at(&str1, i + j) != *VecU8_at(&str2, j))
goto e;
}
return true;
e:
}
return false;
}
bool strings_in_spans_equal(ConstSpanU8 a, ConstSpanU8 b) {
if (a.len != b.len)
return false;
for (size_t i = 0; i < a.len; i++) {
if (*ConstSpanU8_at(a, i) != *ConstSpanU8_at(b, i))
return false;
}
return true;
}
bool string_in_string_vec(ConstSpanU8 a, const VecVecU8* B) {
for (size_t i = 0; i < B->len; i++) {
const VecU8* b = VecVecU8_cat(B, i);
if (strings_in_spans_equal(a, VecU8_to_ConstSpanU8(b)))
return true;
}
return false;
}
#endif

689
src/l2/tests/r0.c Normal file
View File

@ -0,0 +1,689 @@
#include "../margaret/margaret.h"
#include "../../../gen/geom.h"
#include <math.h>
#include "../../l1/system/fileio.h"
#include <time.h>
// Only for linux
#include <sys/wait.h>
typedef struct {
vec3 pos;
vec3 color;
} OA_Vertex;
typedef struct {
vec3 s;
float _0;
} MyUbo;
typedef struct {
VkBuffer vbo;
VkBuffer ebo;
size_t vert_count;
} OA_ObjectOnScene;
#define OA_ObjectOnScene_drop(vp) {}
#define OA_ObjectOnScene_clone(vp) (*(vp))
VecT_trivmove_struct_Definition(OA_ObjectOnScene)
VecT_trivmove_method_Definition(OA_ObjectOnScene)
VecT_primitive_zeroinit_method_Definition(OA_ObjectOnScene)
typedef struct {
VecOA_ObjectOnScene oa_objects;
VkClearColorValue color;
} Scene;
// todo: generate this function in l2
VkRenderPass create_render_pass(VkDevice logical_device, VkFormat image_format) {
// Color attachments array for our render pass
VkAttachmentDescription all_attachments[1] = { (VkAttachmentDescription){
.format = image_format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
} };
// For our one single render subpass
VkAttachmentReference color_attachment_refs[1] = { (VkAttachmentReference){
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
} };
VkSubpassDescription subpasses_descr[1] = { (VkSubpassDescription){
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = ARRAY_SIZE(color_attachment_refs),
.pColorAttachments = color_attachment_refs,
} };
VkSubpassDependency subpass_dependencies[1] = {
// subpass_0_external
(VkSubpassDependency) {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstSubpass = 0,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
}};
VkRenderPassCreateInfo render_pass_crinfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = ARRAY_SIZE(all_attachments),
.pAttachments = all_attachments,
.subpassCount = ARRAY_SIZE(subpasses_descr),
.pSubpasses = subpasses_descr,
.dependencyCount = ARRAY_SIZE(subpass_dependencies),
.pDependencies = subpass_dependencies,
};
VkRenderPass render_pass;
if (vkCreateRenderPass(logical_device, &render_pass_crinfo, NULL, &render_pass) != VK_SUCCESS)
abortf("vkCreateRenderPass");
return render_pass;
}
// todo: generate this class in l2
typedef struct {
VkPipelineLayout pipeline_layout;
VkPipeline pipeline;
VkDescriptorSetLayout descriptor_set_layout;
} PipelineHands;
void destroy_graphics_pipeline_hands(VkDevice device, PipelineHands hands) {
vkDestroyPipeline(device, hands.pipeline, NULL);
vkDestroyPipelineLayout(device, hands.pipeline_layout, NULL);
vkDestroyDescriptorSetLayout(device, hands.descriptor_set_layout, NULL);
}
// todo: generate this function in l2
PipelineHands create_graphics_pipeline(
VkDevice device, VkRenderPass render_pass, uint32_t subpass
) {
VecU8 vert_bin_code = read_whole_file_or_abort("test_shaders/spv/0/vert.spv");
VecU8 frag_bin_code = read_whole_file_or_abort("test_shaders/spv/0/frag.spv");
VkShaderModule vert_module = margaret_VkShaderModule_new(device, vert_bin_code);
VkShaderModule frag_module = margaret_VkShaderModule_new(device, frag_bin_code);
VecU8_drop(vert_bin_code);
VecU8_drop(frag_bin_code);
VkPipelineShaderStageCreateInfo shader_stages_crinfo[2] = {
margaret_shader_stage_vertex_crinfo(vert_module),
margaret_shader_stage_fragment_crinfo(frag_module)
};
VkVertexInputBindingDescription vertex_bindings[1] = { {
.binding = 0,
.stride = sizeof(OA_Vertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
} };
VkVertexInputAttributeDescription vertex_attributes[2] = {
{
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(OA_Vertex, pos),
},
{
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(OA_Vertex, color),
},
};
VkPipelineVertexInputStateCreateInfo vertex_input_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = ARRAY_SIZE(vertex_bindings),
.pVertexBindingDescriptions = vertex_bindings,
.vertexAttributeDescriptionCount = ARRAY_SIZE(vertex_attributes),
.pVertexAttributeDescriptions = vertex_attributes,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkPipelineViewportStateCreateInfo viewport_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
// We are using dynamic viewport and scissors, that is why we do not attach viewport/scissor values
// when creating a rendering pipeline. We will do that later
.viewportCount = 1,
.scissorCount = 1,
};
VkPipelineRasterizationStateCreateInfo rasterizer_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
.lineWidth = 1.0f,
};
VkPipelineMultisampleStateCreateInfo multisampling_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.minSampleShading = 1.0f,
.pSampleMask = NULL,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
// For one framebuffer
VkPipelineColorBlendAttachmentState color_blend_attachments[1] = {(VkPipelineColorBlendAttachmentState){
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE,
}};
// For the entire pipeline
VkPipelineColorBlendStateCreateInfo color_blending_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = ARRAY_SIZE(color_blend_attachments),
.pAttachments = color_blend_attachments,
// Blend constants specified heres
};
VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic_state_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = ARRAY_SIZE(dynamic_states),
.pDynamicStates = dynamic_states,
};
VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = {
// some random binding
{
// Binding in shader
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
// our shader variable is not an array of descriptors, so this stays 1
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
// VkDescriptorSetLayoutBinding {
// .binding = 1,
// .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
// .descriptorCount = 1,
// .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
// },
};
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_crinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = ARRAY_SIZE(bindings_for_my_descr_set_layout),
.pBindings = bindings_for_my_descr_set_layout,
};
VkDescriptorSetLayout my_descriptor_set_layout;
if (vkCreateDescriptorSetLayout(device, &descriptor_set_layout_crinfo, NULL, &my_descriptor_set_layout) != VK_SUCCESS)
abortf("vkCreateDescriptorSetLayout");
VkPipelineLayoutCreateInfo layout_crinfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &my_descriptor_set_layout,
.pushConstantRangeCount = 0,
.pPushConstantRanges = NULL,
};
VkPipelineLayout pipeline_layout;
if (vkCreatePipelineLayout(device, &layout_crinfo, NULL, &pipeline_layout) != VK_SUCCESS)
abortf("vkCreatePipelineLayout");
// todo: kill myself (update: still todo (update: still not done))
VkGraphicsPipelineCreateInfo pipeline_crinfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = ARRAY_SIZE(shader_stages_crinfo),
.pStages = shader_stages_crinfo,
.pVertexInputState = &vertex_input_crinfo,
.pInputAssemblyState = &input_assembly_crinfo,
.pViewportState = &viewport_state,
.pRasterizationState = &rasterizer_crinfo,
.pMultisampleState = &multisampling_crinfo,
.pDepthStencilState = NULL,
.pColorBlendState = &color_blending_crinfo,
.pDynamicState = &dynamic_state_crinfo,
.layout = pipeline_layout,
.renderPass = render_pass,
.subpass = subpass,
.basePipelineHandle = VK_NULL_HANDLE,
};
VkPipeline pipeline;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_crinfo, NULL, &pipeline) != VK_SUCCESS)
abortf("vkCreateGraphicsPipelines");
vkDestroyShaderModule(device, frag_module, NULL);
vkDestroyShaderModule(device, vert_module, NULL);
return (PipelineHands){.pipeline_layout = pipeline_layout, .pipeline = pipeline ,.descriptor_set_layout = my_descriptor_set_layout};
}
void reset_and_record_command_buffer(
VkCommandBuffer command_buffer, VkRenderPass render_pass,
const PipelineHands* pipeline_and_layout,
VkFramebuffer swapchain_image_framebuffer, VkExtent2D image_extent,
const Scene* scene, VkDescriptorSet my_descriptor_set
) {
if (vkResetCommandBuffer(command_buffer, 0) != VK_SUCCESS)
abortf("vkResetCommandBuffer");
VkCommandBufferBeginInfo info_begin = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
if (vkBeginCommandBuffer(command_buffer, &info_begin) != VK_SUCCESS)
abortf("vkBeginCommandBuffer");
VkClearValue clear_color[1] = {{.color = scene->color}};
VkRenderPassBeginInfo renderpass_begin = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = render_pass,
.framebuffer = swapchain_image_framebuffer,
.renderArea.offset = (VkOffset2D){0, 0},
.renderArea.extent = image_extent,
.clearValueCount = ARRAY_SIZE(clear_color),
.pClearValues = clear_color,
};
vkCmdBeginRenderPass(command_buffer, &renderpass_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_and_layout->pipeline);
// We forgot that viewport is not built into our pipeline
// printf("image_extent = {%u, %u}\n", image_extent.width, image_extent.height);
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = (float)(image_extent.width),
.height = (float)(image_extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
// We forgot that scissors are not built into out pipeline
VkRect2D scissor = {
.offset = (VkOffset2D){0, 0},
.extent = image_extent,
};
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
for (size_t i = 0; i < scene->oa_objects.len; i++) {
const OA_ObjectOnScene* obj = VecOA_ObjectOnScene_cat(&scene->oa_objects, i);
VkBuffer attached_buffers[1] = { obj->vbo };
// We use our whole buffer, no need for offset
VkDeviceSize offsets_in_buffers[1] = {0};
vkCmdBindVertexBuffers(command_buffer, 0, 1, attached_buffers, offsets_in_buffers);
vkCmdBindIndexBuffer(command_buffer, obj->ebo, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_and_layout->pipeline_layout, 0,
1, &my_descriptor_set, 0, NULL);
vkCmdDrawIndexed(command_buffer, obj->vert_count, 1, 0, 0, 0);
}
vkCmdEndRenderPass(command_buffer);
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS)
abortf("vkEndCommandBuffer");
}
void recreate_swapchain(
VkPhysicalDevice physical_device, VkDevice device, MargaretChosenQueueFamilies queue_fam, VkSurfaceKHR surface,
VkRenderPass render_pass, MargaretSwapchainBundle* swfb) {
// We are about stop program and rebuild our sem+sem+fence synchronization mechanism
vkDeviceWaitIdle(device);
VkSwapchainKHR old_swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(device, *swfb);
// old swfb is 83% dropped
ResultMargaretChosenSwapchainDetailsOrConstSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface);
assert(swapchain_details_res.variant == Result_Ok);
MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok;
MargaretSwapchainBundle new_swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details, surface,
render_pass, swfb->swapchain);
vkDestroySwapchainKHR(device, old_swapchain, NULL);
// Now old swfb is 100% dropped
*swfb = new_swfb;
}
margaret_prep_buffer_mem_info_of_gpu_vbo_Definition(OA_Vertex)
void prepare_shaders() {
int ret = system("./test_shader_compile.sh");
if (ret == -1) {
perror("system() failed");
exit(EXIT_FAILURE);
} else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) {
fprintf(stderr, "Error: script exited with code %d\n", WEXITSTATUS(ret));
exit(EXIT_FAILURE);
} else if (!WIFEXITED(ret)) {
fprintf(stderr, "Error: script terminated abnormally\n");
exit(EXIT_FAILURE);
}
}
int main() {
prepare_shaders();
ConstSpanU8 GPU = cstr("amd");
ConstSpanU8 bugged_GPU = cstr("nvidia");
bool ENABLE_VALIDATION_LAYERS = true;
// U32 MAX_WIN_WIDTH = 1900;
// U32 MAX_WIN_HEIGHT = 800;
// int MAX_FRAMES_IN_FLIGHT = 2;
MargaretSingleWindowSetup x = MargaretSingleWindowSetup_new();
Margaret_WEP wep = Margaret_WEP_new(x.dpy, x.win);
XMapWindow(x.dpy, x.win);
MargaretInstanceAndItsDebug inst_hands = MargaretInstanceAndItsDebug_new(ENABLE_VALIDATION_LAYERS);
VkInstance instance = inst_hands.instance;
// print_instance_available_extensions(instance);
// print_instance_available_layers(instance);
VkSurfaceKHR surface = margaret_create_surface(instance, &x);
VkPhysicalDevice physical_device = margaret_select_one_physical_device(instance, surface, GPU, bugged_GPU);
// print_physical_device_available_extensions(physical_device);
ResultMargaretChosenQueueFamiliesOrConstSpanU8 queue_fam_res = margaret_choose_good_queue_families(physical_device, surface);
assert(queue_fam_res.variant == Result_Ok);
MargaretChosenQueueFamilies queue_fam = queue_fam_res.ok;
VkDevice device = margaret_create_logical_device(physical_device, queue_fam);
VkQueue graphics_queue;
vkGetDeviceQueue(device, queue_fam.for_graphics, 0, &graphics_queue);
VkQueue presentation_queue;
vkGetDeviceQueue(device, queue_fam.for_graphics, 0, &presentation_queue);
ResultMargaretChosenSwapchainDetailsOrConstSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface);
assert(swapchain_details_res.variant == Result_Ok);
MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok;
// We hope that the image format won't be changed even when window gets resized
// VkSurfaceFormatKHR image_format = choose_surface_format_i_want(swap_chain_support).value();
VkRenderPass render_pass = create_render_pass(device, swapchain_details.surface_format.format);
PipelineHands pipeline_hands = create_graphics_pipeline(device, render_pass, 0);
MargaretSwapchainBundle swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details, surface, render_pass, NULL);
// Filling scene info
OA_Vertex obj1_vertexes[] = {
(OA_Vertex){ .pos = {-0.6f, -1.0f, 0}, .color = {1.f, 0, 0} },
(OA_Vertex){ .pos = {-0.8f, -0.8f, 0}, .color = {0, 1.f, 0} },
(OA_Vertex){ .pos = {-0.8f, -0.6f, 0}, .color = {0, 0, 1.f} },
};
uint32_t obj1_indices[] = { 0, 1, 2 };
OA_Vertex obj2_vertexes[] = {
(OA_Vertex){ .pos = {0.9f, 0.9f}, .color = {1.f, 0, 0} },
(OA_Vertex){ .pos = {0.4f, -0.9f, 0}, .color = {0, 1.f, 0} },
(OA_Vertex){ .pos = {-0.2f, 1.f}, .color = {0, 0, 1.f} },
};
uint32_t obj2_indices[] = {0, 1, 2};
// todo: add a texture into the mix
// We have only one staging buffer in host memory (because we don't really need more)
MargaretBufferInMemoryInfo host_mem_buffer = (MargaretBufferInMemoryInfo){ .sz =
MAX_U64(sizeof(obj1_vertexes),
MAX_U64(sizeof(obj1_indices),
MAX_U64(sizeof(obj2_vertexes),
MAX_U64(sizeof(obj2_indices),
MAX_U64(sizeof(MyUbo), 0)))))
, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT };
VkDeviceMemory host_mem = margaret_initialize_buffers_and_images(physical_device, device,
(SpanMargaretBufferInMemoryInfo){.data = &host_mem_buffer, .len = 1}, (SpanMargaretImageInMemoryInfo){ 0 },
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
MargaretBufferInMemoryInfo device_mem_buffers[] = {
OA_Vertex_buffer_crinfo_of_gpu_vbo(ARRAY_SIZE(obj1_vertexes)),
margaret_prep_buffer_mem_info_of_gpu_ebo(ARRAY_SIZE(obj1_indices)),
OA_Vertex_buffer_crinfo_of_gpu_vbo(ARRAY_SIZE(obj2_vertexes)),
margaret_prep_buffer_mem_info_of_gpu_ebo(ARRAY_SIZE(obj2_indices)),
margaret_prep_buffer_mem_info_of_gpu_ubo(sizeof(MyUbo)),
};
VkDeviceMemory device_mem = margaret_initialize_buffers_and_images(physical_device, device,
(SpanMargaretBufferInMemoryInfo){ .data = device_mem_buffers, .len = ARRAY_SIZE(device_mem_buffers)},
(SpanMargaretImageInMemoryInfo){ 0 }, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
MargaretBufferInMemoryInfo device_vbo_1_buffer = device_mem_buffers[0];
MargaretBufferInMemoryInfo device_ebo_1_buffer = device_mem_buffers[1];
MargaretBufferInMemoryInfo device_vbo_2_buffer = device_mem_buffers[2];
MargaretBufferInMemoryInfo device_ebo_2_buffer = device_mem_buffers[3];
MargaretBufferInMemoryInfo device_ubo_my_buffer = device_mem_buffers[4];
// device_mem_buffers may be considered invalidated, forgotten you might say
VkCommandPool command_pool = margaret_create_resettable_command_pool(device, queue_fam.for_graphics);
VkCommandBuffer rendering_command_buffer = margaret_allocate_command_buffer(device, command_pool);
VkCommandBuffer uniform_transfer_command_buffer = margaret_allocate_command_buffer(device, command_pool);
margaret_record_buf_copying_command_buf(device, uniform_transfer_command_buffer,
device_ubo_my_buffer.buffer, host_mem_buffer.buffer, sizeof(MyUbo));
void* host_mem_buffer_mem;
if (vkMapMemory(device, host_mem, 0, VK_WHOLE_SIZE, 0, &host_mem_buffer_mem) != VK_SUCCESS)
abortf("vkMapMemory");
// Now this is what we will do for each buffer: we first memcpy it into mapped region, then we submit a copying command
memcpy(host_mem_buffer_mem, obj1_vertexes, sizeof(obj1_vertexes));
margaret_copy_buffer_imm(device, command_pool, graphics_queue,
device_vbo_1_buffer.buffer, host_mem_buffer.buffer, sizeof(obj1_vertexes));
memcpy(host_mem_buffer_mem, obj1_indices, sizeof(obj1_indices));
margaret_copy_buffer_imm(device, command_pool, graphics_queue,
device_ebo_1_buffer.buffer, host_mem_buffer.buffer, sizeof(obj1_indices));
memcpy(host_mem_buffer_mem, obj2_vertexes, sizeof(obj2_vertexes));
margaret_copy_buffer_imm(device, command_pool, graphics_queue,
device_vbo_2_buffer.buffer, host_mem_buffer.buffer, sizeof(obj2_vertexes));
memcpy(host_mem_buffer_mem, obj2_indices, sizeof(obj2_indices));
margaret_copy_buffer_imm(device, command_pool, graphics_queue,
device_ebo_2_buffer.buffer, host_mem_buffer.buffer, sizeof(obj2_indices));
// We sent everything we needed. but host_mem_buffer_mem may be used later
Scene scene;
scene.oa_objects = VecOA_ObjectOnScene_new_zeroinit(2);
*VecOA_ObjectOnScene_at(&scene.oa_objects, 0) = (OA_ObjectOnScene){
.vbo = device_vbo_1_buffer.buffer, .ebo = device_ebo_1_buffer.buffer, .vert_count = ARRAY_SIZE(obj1_vertexes) };
*VecOA_ObjectOnScene_at(&scene.oa_objects, 1) = (OA_ObjectOnScene){
.vbo = device_vbo_2_buffer.buffer, .ebo = device_ebo_2_buffer.buffer, .vert_count = ARRAY_SIZE(obj2_vertexes) };
// device_vob/ebo_1/2_buffer won't be used anymore
VkDescriptorPool descriptor_pool = margaret_create_descriptor_set_pool(device, 1, 0, 1);
VkDescriptorSetAllocateInfo descriptor_sets_alloc_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = &pipeline_hands.descriptor_set_layout,
};
VkDescriptorSet my_descriptor_set;
if (vkAllocateDescriptorSets(device, &descriptor_sets_alloc_info, &my_descriptor_set) != VK_SUCCESS)
abortf("vkAllocateDescriptorSets");
// Configuring my descriptor set (of one single descriptor set layout from my pipeline)
VkDescriptorBufferInfo buffer_info_for_descriptor_0 = {
.buffer = device_ubo_my_buffer.buffer,
.offset = 0,
.range = sizeof(MyUbo),
};
// VkDescriptorImageInfo image_info_for_descriptor_1 {
// .sampler = my_texture_sampler,
// .imageView = my_texture_image_view,
// .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
// };
VkWriteDescriptorSet descriptor_writes[] = {
(VkWriteDescriptorSet){
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = my_descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &buffer_info_for_descriptor_0,
},
// VkWriteDescriptorSet{
// .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
// .dstSet = descriptor_sets_for_ubo[i],
// .dstBinding = 1,
// .dstArrayElement = 0,
// .descriptorCount = 1,
// .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
// .pImageInfo = &image_info_for_descriptor_1,
// },
};
vkUpdateDescriptorSets(device, ARRAY_SIZE(descriptor_writes), descriptor_writes, 0, NULL);
// Mainloop
margaret_ns_time start = margaret_clock_gettime_monotonic_raw();
margaret_ns_time prev_key_frame_time = start;
int frame_count_since_key = 0;
// margaret_ns_time prev_timestamp = margaret_clock_gettime_monotonic_raw();
while (true) {
margaret_ns_time frame_A0 = margaret_clock_gettime_monotonic_raw();
VecXlib_Event events = margaret_read_x_events(x.dpy);
for (size_t i = 0; i < events.len; i++)
Margaret_WEP_update_with_new_event(&wep, VecXlib_Event_cat(&events, i));
if (wep.should_stop)
break;
// Rendering
vkWaitForFences(device, 1, &swfb.in_flight_fence, VK_TRUE, UINT64_MAX);
uint32_t ij;
VkResult aq_ret = vkAcquireNextImageKHR(
device, swfb.swapchain,
UINT64_MAX, swfb.image_available_semaphore, VK_NULL_HANDLE, &ij
);
if (aq_ret == VK_ERROR_OUT_OF_DATE_KHR) {
fprintf(stderr, "vkAcquireNextImageKHR: VK_ERROR_OUT_OF_DATE_KHR\n");
recreate_swapchain(physical_device, device, queue_fam, surface, render_pass, &swfb);
continue;
} else if (aq_ret == VK_SUBOPTIMAL_KHR) {
fprintf(stderr, "vkAcquireNextImageKHR: VK_SUBOPTIMAL_KHR\n");
recreate_swapchain(physical_device, device, queue_fam, surface, render_pass, &swfb);
continue;
} else if (aq_ret != VK_SUCCESS) {
abortf("vkAcquireNextImageKHR");
}
vkResetFences(device, 1, &swfb.in_flight_fence);
float ae = sinf(margaret_ns_time_sec_diff(start, frame_A0));
scene.color = (VkClearColorValue){{0.5f, fabsf(ae), .3f, 1.0f}};
vec3 SS = {ae * ae, 0.5f + 0.5f * ae, 0};
{
*(MyUbo *)host_mem_buffer_mem = (MyUbo){ .s = SS };
VkCommandBuffer command_buffers[1] = { uniform_transfer_command_buffer };
VkSemaphore signaling_semaphores[1] = { swfb.in_frame_transfer_complete };
VkSubmitInfo ubo_copying_cmd_buffer_submit = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = ARRAY_SIZE(command_buffers),
.pCommandBuffers = command_buffers,
.signalSemaphoreCount = ARRAY_SIZE(signaling_semaphores),
.pSignalSemaphores = signaling_semaphores,
};
vkQueueSubmit(graphics_queue, 1, &ubo_copying_cmd_buffer_submit, NULL);
}
reset_and_record_command_buffer(rendering_command_buffer, render_pass, &pipeline_hands,
*VecVkFramebuffer_cat(&swfb.framebuffers, ij), swfb.extent, &scene, my_descriptor_set);
{
VkSemaphore waiting_for_semaphores[2] = {
swfb.image_available_semaphore, swfb.in_frame_transfer_complete
};
VkPipelineStageFlags waiting_stages[2] = {
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
};
assert(ARRAY_SIZE(waiting_for_semaphores) == ARRAY_SIZE(waiting_stages));
// VkCommandBuffer command_buffers[1] = {*VecVkCommandBuffer_cat(&rendering_command_buffers, ij)};
VkCommandBuffer command_buffers[1] = {rendering_command_buffer};
VkSemaphore signaling_semaphores[1] = { swfb.render_finished_semaphore };
VkSubmitInfo cmd_submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
// We wait for `waiting_for_semaphores` before THESE stages
// waitSemaphoreCount specifies size for both pWaitSemaphores and pWaitDstStageMask
.waitSemaphoreCount = ARRAY_SIZE(waiting_for_semaphores),
.pWaitSemaphores = waiting_for_semaphores,
.pWaitDstStageMask = waiting_stages,
.commandBufferCount = ARRAY_SIZE(command_buffers),
.pCommandBuffers = command_buffers,
.signalSemaphoreCount = ARRAY_SIZE(signaling_semaphores),
.pSignalSemaphores = signaling_semaphores,
};
if (vkQueueSubmit(graphics_queue, 1, &cmd_submit_info, swfb.in_flight_fence) != VK_SUCCESS)
abortf("vkQueueSubmit");
}
{
VkSemaphore waiting_for_semaphores[] = { swfb.render_finished_semaphore };
VkSwapchainKHR swapchains[] = { swfb.swapchain };
uint32_t image_indices[] = { ij };
assert( ARRAY_SIZE(swapchains) == ARRAY_SIZE(image_indices) );
VkPresentInfoKHR present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = ARRAY_SIZE(waiting_for_semaphores),
.pWaitSemaphores = waiting_for_semaphores,
.swapchainCount = ARRAY_SIZE(swapchains),
.pSwapchains = swapchains,
.pImageIndices = image_indices,
.pResults = NULL,
};
VkResult pres_ret = vkQueuePresentKHR(presentation_queue, &present_info);
if (pres_ret == VK_ERROR_OUT_OF_DATE_KHR) {
fprintf(stderr, "vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR\n");
recreate_swapchain(physical_device, device, queue_fam, surface, render_pass, &swfb);
continue;
} else if (pres_ret == VK_SUBOPTIMAL_KHR) {
fprintf(stderr, "vkQueuePresentKHR: VK_SUBOPTIMAL_KHR\n");
recreate_swapchain(physical_device, device, queue_fam, surface, render_pass, &swfb);
continue;
} else if (pres_ret != VK_SUCCESS) {
abortf("vkQueuePresentKHR");
}
}
margaret_ns_time frame_B0 = margaret_clock_gettime_monotonic_raw();
if (margaret_ns_time_sec_diff(frame_A0, frame_B0) > 0.3) {
fprintf(stderr, "]]] Profiling frame scheduling:\n"
"total: %.6lf\n"
"]]]",
margaret_ns_time_sec_diff(frame_A0, frame_B0));
}
frame_count_since_key++;
if (margaret_ns_time_sec_diff(prev_key_frame_time, frame_B0) > 1.0) {
float fps = (float)frame_count_since_key / margaret_ns_time_sec_diff(prev_key_frame_time, frame_B0);
printf("FPS: %0.1lf\n", fps);
frame_count_since_key = 0;
prev_key_frame_time = frame_B0;
}
}
vkDeviceWaitIdle(device);
// The End
// dropping scene
VecOA_ObjectOnScene_drop(scene.oa_objects);
// destroying vulkan objects
vkDestroyDescriptorPool(device, descriptor_pool, NULL);
for (size_t i = 0; i < ARRAY_SIZE(device_mem_buffers); i++)
vkDestroyBuffer(device, device_mem_buffers[i].buffer, NULL);
vkDestroyBuffer(device, host_mem_buffer.buffer, NULL);
vkFreeMemory(device, device_mem, NULL);
vkUnmapMemory(device, host_mem);
vkFreeMemory(device, host_mem, NULL);
vkDestroyCommandPool(device, command_pool, NULL);
MargaretSwapchainBundle_drop_with_device(device, swfb);
destroy_graphics_pipeline_hands(device, pipeline_hands);
vkDestroyRenderPass(device, render_pass, NULL);
vkDestroyDevice(device, NULL);
vkDestroySurfaceKHR(instance, surface, NULL);
MargaretInstanceAndItsDebug_drop(inst_hands);
MargaretSingleWindowSetup_drop(x);
}

View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd test_shaders
glslc -o spv/0/vert.spv glsl/0/0.vert
glslc -o spv/0/frag.spv glsl/0/0.frag

View File

@ -0,0 +1,13 @@
#version 450
layout(location = 0) in vec3 fsin_color;
layout(location = 0) out vec4 fin_color;
layout(binding = 0) uniform my_ubo {
vec3 s; // 0 + 12 + 4
};
void main() {
fin_color = vec4(fsin_color * s, 1.0);
}

View File

@ -0,0 +1,23 @@
#version 450
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 vsout_color;
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
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)
);
void main() {
gl_Position = vec4(pos, 1.0);
vsout_color = color;
}