summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/triangle/ex_triangle.c34
-rw-r--r--src/log.h12
-rw-r--r--src/renderer/backends/backend_vulkan.c143
-rw-r--r--src/renderer/backends/backend_vulkan.h11
-rw-r--r--src/renderer/ral.h31
-rw-r--r--src/renderer/ral_types.h14
-rw-r--r--src/renderer/render.c25
-rw-r--r--src/renderer/render_types.h5
-rw-r--r--src/std/buf.h17
-rw-r--r--xmake.lua11
10 files changed, 267 insertions, 36 deletions
diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c
new file mode 100644
index 0000000..4e31313
--- /dev/null
+++ b/examples/triangle/ex_triangle.c
@@ -0,0 +1,34 @@
+#include <glfw3.h>
+
+#include "camera.h"
+#include "core.h"
+#include "maths.h"
+#include "render.h"
+
+int main() {
+ core* core = core_bringup();
+
+ camera camera = camera_create(vec3_create(0, 0, 20), VEC3_NEG_Z, VEC3_Y, deg_to_rad(45.0));
+
+ // Main loop
+ while (!glfwWindowShouldClose(core->renderer.window)) {
+ input_update(&core->input);
+ // threadpool_process_results(&core->threadpool, 1);
+
+ render_frame_begin(&core->renderer);
+
+ static f32 x = 0.0;
+ x += 0.01;
+ mat4 model = mat4_translation(vec3(x, 0, 0));
+
+ gfx_backend_draw_frame(&core->renderer, &camera, model, NULL);
+
+ // insert work here
+
+ render_frame_end(&core->renderer);
+ glfwSwapBuffers(core->renderer.window);
+ glfwPollEvents();
+ }
+
+ return 0;
+}
diff --git a/src/log.h b/src/log.h
index b0c355b..d954684 100644
--- a/src/log.h
+++ b/src/log.h
@@ -38,19 +38,19 @@ void logger_shutdown();
void log_output(log_level level, const char* message, ...);
-#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__);
-#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__);
-#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__);
-#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__);
+#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__)
+#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__)
+#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__)
+#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__)
#if LOG_DEBUG_ENABLED == 1
-#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__);
+#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__)
#else
#define DEBUG(message, ...)
#endif
#if LOG_TRACE_ENABLED == 1
-#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__);
+#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__)
#else
#define TRACE(message, ...)
#endif \ No newline at end of file
diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c
index b66aeca..2502dd2 100644
--- a/src/renderer/backends/backend_vulkan.c
+++ b/src/renderer/backends/backend_vulkan.c
@@ -1,39 +1,109 @@
+#include <glfw3.h>
+#include <stdlib.h>
#include <vulkan/vk_platform.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_core.h>
#include "backend_vulkan.h"
+#include "mem.h"
+#include "vulkan_helpers.h"
#include "defines.h"
#include "log.h"
#include "ral.h"
#include "ral_types.h"
+// TEMP
+#define SCREEN_WIDTH 1000
+#define SCREEN_HEIGHT 1000
+
#define VULKAN_QUEUES_COUNT 2
const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" };
typedef struct vulkan_context {
- gpu_device device;
+ VkInstance instance;
VkAllocationCallbacks* allocator;
+ VkSurfaceKHR surface;
- VkInstance instance;
+ arena temp_arena;
+ gpu_device* device;
+ gpu_swapchain* swapchain;
+ u32 screen_width;
+ u32 screen_height;
} vulkan_context;
static vulkan_context context;
-static bool select_physical_device(gpu_device* out_device) {}
+// --- Function forward declarations
+
+/** @brief Enumerates and selects the most appropriate graphics device */
+bool select_physical_device(gpu_device* out_device);
+/** @brief Helper function for creating array of all extensions we want */
+cstr_darray* get_all_extensions();
+
+bool gpu_backend_init(const char* window_name, GLFWwindow* window) {
+ context.allocator = 0; // TODO: use an allocator
+ context.screen_width = SCREEN_WIDTH;
+ context.screen_height = SCREEN_HEIGHT;
+
+ // Create an allocator
+ size_t temp_arena_size = 1024 * 1024;
+ arena_create(malloc(temp_arena_size), temp_arena_size);
+
+ // Setup Vulkan instance
+ VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
+ app_info.apiVersion = VK_API_VERSION_1_3;
+ app_info.pApplicationName = window_name;
+ app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
+ app_info.pEngineName = "Celeritas Engine";
+ app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+
+ VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+ create_info.pApplicationInfo = &app_info;
+
+ // Extensions
+ // FIXME: Use my own extension choices
+ // cstr_darray* required_extensions = cstr_darray_new(2);
+ // cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME);
+ // create_info.enabledExtensionCount = cstr_darray_len(required_extensions);
+ // create_info.ppEnabledExtensionNames = required_extensions->data;
+ uint32_t count;
+ const char** extensions = glfwGetRequiredInstanceExtensions(&count);
+ create_info.enabledExtensionCount = count;
+ create_info.ppEnabledExtensionNames = extensions;
+
+ // TODO: Validation layers
+ create_info.enabledLayerCount = 0;
+ create_info.ppEnabledLayerNames = NULL;
+
+ VkResult result = vkCreateInstance(&create_info, NULL, &context.instance);
+ if (result != VK_SUCCESS) {
+ ERROR("vkCreateInstance failed with result: %u", result);
+ return false;
+ }
+ TRACE("Vulkan Instance created");
+
+ // Surface creation
+ VkSurfaceKHR surface;
+ VK_CHECK(glfwCreateWindowSurface(context.instance, window, NULL, &surface));
+ context.surface = surface;
+ TRACE("Vulkan Surface created");
-gpu_device gpu_device_create() {
- gpu_device device = { 0 };
+ return true;
+}
+
+void gpu_backend_shutdown() { arena_free_storage(&context.temp_arena); }
+
+bool gpu_device_create(gpu_device* out_device) {
// Physical device
- // if (!select_physical_device()) {
- // return false;
- // }
- INFO("Physical device selected");
+ if (!select_physical_device(out_device)) {
+ return false;
+ }
+ TRACE("Physical device selected");
// Features
- VkPhysicalDeviceFeatures device_features = {};
+ VkPhysicalDeviceFeatures device_features = { 0 };
device_features.samplerAnisotropy = VK_TRUE; // request anistrophy
// Logical device
@@ -47,20 +117,59 @@ gpu_device gpu_device_create() {
const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
device_create_info.ppEnabledExtensionNames = &extension_names;
- VkResult result = vkCreateDevice(device.physical_device, &device_create_info, context.allocator,
- &device.logical_device);
+ VkResult result = vkCreateDevice(out_device->physical_device, &device_create_info,
+ context.allocator, &out_device->logical_device);
if (result != VK_SUCCESS) {
FATAL("Error creating logical device with status %u\n", result);
exit(1);
}
- INFO("Logical device created");
+ TRACE("Logical device created");
// Queues
// Create the command pool
- context.device = device;
- return device;
+ return true;
+}
+
+bool gpu_swapchain_create(gpu_swapchain* out_swapchain) {
+ VkExtent2D swapchain_extent = { context.screen_width, context.screen_height };
+
+ // find a format
+
+ VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented
+
+ VkSwapchainCreateInfoKHR swapchain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
+
+ // swapchain_create_info.minImageCount =
+
+ VK_CHECK(vkCreateSwapchainKHR(context.device->logical_device, &swapchain_create_info,
+ context.allocator, &out_swapchain->handle));
+ TRACE("Vulkan Swapchain created");
+}
+
+gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) {
+ VkViewport viewport = { .x = 0,
+ .y = 0,
+ .width = (f32)context.screen_width,
+ .height = (f32)context.screen_height,
+ .minDepth = 0.0,
+ .maxDepth = 1.0 };
+ VkRect2D scissor = { .offset = { .x = 0, .y = 0 },
+ .extent = { .width = context.screen_width,
+ .height = context.screen_height } };
+
+ // TODO: Attributes
+
+ // TODO: layouts
+
+ VkPipelineViewportStateCreateInfo viewport_state = {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
+ };
+ viewport_state.viewportCount = 1;
+ viewport_state.pViewports = &viewport;
+ viewport_state.scissorCount = 1;
+ viewport_state.pScissors = &scissor;
}
gpu_renderpass* gpu_renderpass_create() {
@@ -81,4 +190,6 @@ void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline) {
// --- Drawing
inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) {
vkCmdDrawIndexed(encoder->cmd_buffer, index_count, 1, 0, 0, 0);
-} \ No newline at end of file
+}
+
+bool select_physical_device(gpu_device* out_device) {} \ No newline at end of file
diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h
index 05f043e..dfe6a0f 100644
--- a/src/renderer/backends/backend_vulkan.h
+++ b/src/renderer/backends/backend_vulkan.h
@@ -1,9 +1,20 @@
#pragma once
+#include <vulkan/vk_platform.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+
#include "defines.h"
#define GPU_SWAPCHAIN_IMG_COUNT 2
+/*
+Conventions:
+ - Place the 'handle' as the first field of a struct
+ - Vulkan specific data goes at the top, followed by our internal data
+*/
+
typedef struct gpu_swapchain {
+ VkSwapchainKHR handle;
} gpu_swapchain;
typedef struct gpu_device {
// In Vulkan we store both physical and logical device here
diff --git a/src/renderer/ral.h b/src/renderer/ral.h
index fb77f0a..014c31e 100644
--- a/src/renderer/ral.h
+++ b/src/renderer/ral.h
@@ -14,6 +14,11 @@
#include "ral_types.h"
#include "defines.h"
#include "str.h"
+#include "buf.h"
+
+// Unrelated forward declares
+typedef struct arena arena;
+struct GLFWwindow;
// Forward declare structs
typedef struct gpu_swapchain gpu_swapchain;
@@ -34,21 +39,28 @@ typedef struct shader_desc {
str8 glsl; // contents
} shader_desc;
-struct pipeline_desc {
+struct graphics_pipeline_desc {
shader_desc vs; /** @brief Vertex shader stage */
shader_desc fs; /** @brief Fragment shader stage */
};
-// lifecycle functions
-gpu_device gpu_device_create();
+// --- Lifecycle functions
+
+bool gpu_backend_init(const char* window_name, struct GLFWwindow* window);
+void gpu_backend_shutdown();
+
+bool gpu_device_create(gpu_device* out_device);
void gpu_device_destroy();
gpu_renderpass* gpu_renderpass_create();
void gpu_renderpass_destroy(gpu_renderpass* pass);
-gpu_pipeline* gpu_pipeline_create(enum pipeline_kind kind, struct pipeline_desc description);
+gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description);
void gpu_pipeline_destroy(gpu_pipeline* pipeline);
+bool gpu_swapchain_create(gpu_swapchain* out_swapchain);
+void gpu_swapchain_destroy();
+
void gpu_cmd_encoder_begin();
void gpu_cmd_encoder_begin_render();
void gpu_cmd_encoder_begin_compute();
@@ -58,6 +70,10 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off
buffer_handle dst, u64 dst_offset, u64 copy_size);
void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline);
+
+/** @brief Upload CPU-side data as array of bytes to a GPU buffer */
+void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size);
+
// render pass
void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
@@ -84,4 +100,9 @@ void gpu_texture_destroy();
void gpu_texture_upload();
// Samplers
-void gpu_sampler_create(); \ No newline at end of file
+void gpu_sampler_create();
+
+// --- Vertex formats
+bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* vertices);
+
+// TODO: Bindgroup texture samplers / shader resources \ No newline at end of file
diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h
index a20e600..73b41f1 100644
--- a/src/renderer/ral_types.h
+++ b/src/renderer/ral_types.h
@@ -50,9 +50,9 @@ typedef enum gpu_texture_format {
/** @brief Texture Description - used by texture creation functions */
typedef struct texture_desc {
- // gpu_texture_type tex_type;
- // gpu_texture_format format;
- // u32x2 extents;
+ gpu_texture_type tex_type;
+ gpu_texture_format format;
+ u32x2 extents;
} texture_desc;
typedef enum vertex_format {
@@ -65,7 +65,6 @@ typedef enum vertex_format {
typedef union vertex {
struct {
vec3 position;
- vec4 colour;
vec2 tex_coords;
vec3 normal;
} static_3d; /** @brief standard vertex format for static geometry in 3D */
@@ -84,6 +83,13 @@ typedef union vertex {
vec4i bone_ids; // Integer vector for bone IDs
vec4 bone_weights; // Weight of each bone's influence
} skinned_3d; /** @brief vertex format for skeletal (animated) geometry in 3D */
+
+ struct {
+ vec3 position;
+ vec2 tex_coords;
+ vec3 normal;
+ vec4 colour;
+ } coloured_static_3d; /** @brief vertex format used for debugging */
} vertex;
#ifndef TYPED_VERTEX_ARRAY
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 034585a..799cba7 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,6 +1,12 @@
#include "render.h"
#include <glfw3.h>
#include "camera.h"
+#include "log.h"
+#include "ral.h"
+
+/** @brief Creates the pipelines built into Celeritas such as rendering static opaque geometry,
+ debug visualisations, immediate mode UI, etc */
+void default_pipelines_init(renderer* ren);
bool renderer_init(renderer* ren) {
// INFO("Renderer init");
@@ -29,12 +35,20 @@ bool renderer_init(renderer* ren) {
glfwMakeContextCurrent(ren->window);
+ DEBUG("Start backend init");
+
+ gpu_backend_init("Celeritas Engine - Vulkan", window);
+ gpu_device_create(&ren->device); // TODO: handle errors
+ gpu_swapchain_create(&ren->swapchain);
+
// DEBUG("init graphics api backend");
// if (!gfx_backend_init(ren)) {
// FATAL("Couldnt load graphics api backend");
// return false;
// }
+ default_pipelines_init(ren);
+
// ren->blinn_phong =
// shader_create_separate("assets/shaders/blinn_phong.vert",
// "assets/shaders/blinn_phong.frag");
@@ -46,7 +60,16 @@ bool renderer_init(renderer* ren) {
return true;
}
-void renderer_shutdown(renderer* ren) {}
+void renderer_shutdown(renderer* ren) {
+ // gpu_device_destroy(ren->device);
+}
+
+void default_pipelines_init(renderer* ren) {
+ // Static opaque geometry
+ // graphics_pipeline_desc gfx = {
+ // };
+ // ren->static_opaque_pipeline = gpu_graphics_pipeline_create();
+}
void render_frame_begin(renderer* ren) {}
void render_frame_end(renderer* ren) {}
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index 5faefad..6ef2461 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -29,8 +29,9 @@ typedef struct renderer {
struct GLFWwindow* window;
void* backend_context;
renderer_config config;
- gpu_device* device;
- gpu_pipeline* static_opaque_pipeline;
+ gpu_device device;
+ gpu_swapchain swapchain;
+ gpu_pipeline static_opaque_pipeline;
} renderer;
typedef struct geometry_data {
diff --git a/src/std/buf.h b/src/std/buf.h
new file mode 100644
index 0000000..b0f8b85
--- /dev/null
+++ b/src/std/buf.h
@@ -0,0 +1,17 @@
+/**
+ * @file buf.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-28
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "defines.h"
+
+typedef struct bytebuffer {
+ u8* buf;
+ size_t size;
+} bytebuffer;
diff --git a/xmake.lua b/xmake.lua
index 6fee04a..ab6a7a6 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -143,11 +143,18 @@ target("core_shared")
add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds
end
-target("main_loop")
+-- target("main_loop")
+-- set_kind("binary")
+-- set_group("examples")
+-- add_deps("core_static")
+-- add_files("examples/main_loop/ex_main_loop.c")
+-- set_rundir("$(projectdir)")
+
+target("tri")
set_kind("binary")
set_group("examples")
add_deps("core_static")
- add_files("examples/main_loop/ex_main_loop.c")
+ add_files("examples/triangle/ex_triangle.c")
set_rundir("$(projectdir)")
-- target("std")