From d43a48d4855cdd35b4aad2c1dd0190336a0ec0e6 Mon Sep 17 00:00:00 2001 From: mcc Date: Thu, 13 Feb 2020 14:54:39 -0500 Subject: [PATCH 01/44] First attempt GLFW gamepad driver --- CMakeLists.txt | 6 ++ src/api/l_headset.c | 1 + src/core/os_glfw.h | 5 + src/modules/headset/gamepad.c | 189 ++++++++++++++++++++++++++++++++++ src/modules/headset/headset.c | 3 + src/modules/headset/headset.h | 10 +- 6 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/modules/headset/gamepad.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fd9c0571f..9130a3b7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(LOVR_USE_OCULUS "Enable the LibOVR backend for the headset module (be sur option(LOVR_USE_OCULUS_MOBILE "Enable the Oculus Mobile (Android) backend for the headset module" OFF) option(LOVR_USE_DESKTOP_HEADSET "Enable the keyboard/mouse backend for the headset module" ON) option(LOVR_USE_LEAP "Enable the Leap Motion backend for the headset module" OFF) +option(LOVR_USE_GAMEPAD "Enable the gamepad backend for desktop systems" ON) option(LOVR_SYSTEM_ENET "Use the system-provided enet" OFF) option(LOVR_SYSTEM_GLFW "Use the system-provided glfw" OFF) @@ -72,6 +73,7 @@ elseif(ANDROID) set(LOVR_USE_OCULUS OFF) set(LOVR_USE_DESKTOP_HEADSET OFF) set(LOVR_USE_OCULUS_MOBILE ON) + set(LOVR_USE_GAMEPAD OFF) # Requires GLFW currently set(LOVR_BUILD_SHARED ON) # Android has only "activities" elseif(UNIX) find_package(PkgConfig) @@ -480,6 +482,10 @@ if(LOVR_ENABLE_HEADSET) add_definitions(-DLOVR_USE_LEAP) target_sources(lovr PRIVATE src/modules/headset/leap.c) endif() + if(LOVR_USE_GAMEPAD) + add_definitions(-DLOVR_USE_GAMEPAD) + target_sources(lovr PRIVATE src/modules/headset/gamepad.c) + endif() endif() if(LOVR_ENABLE_MATH) diff --git a/src/api/l_headset.c b/src/api/l_headset.c index a9711d1f9..dc1856b3e 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -35,6 +35,7 @@ const char* Devices[] = { [DEVICE_HAND_RIGHT] = "hand/right", [DEVICE_EYE_LEFT] = "eye/left", [DEVICE_EYE_RIGHT] = "eye/right", + [DEVICE_GAMEPAD_1] = "gamepad/1", NULL }; diff --git a/src/core/os_glfw.h b/src/core/os_glfw.h index df62b6bed..cb81ad999 100644 --- a/src/core/os_glfw.h +++ b/src/core/os_glfw.h @@ -10,6 +10,9 @@ #include #endif +#ifdef OS_GLFW_H +#define OS_GLFW_H + static struct { GLFWwindow* window; windowCloseCallback onWindowClose; @@ -252,3 +255,5 @@ HGLRC lovrPlatformGetContext() { return glfwGetWGLContext(glfwState.window); } #endif + +#endif \ No newline at end of file diff --git a/src/modules/headset/gamepad.c b/src/modules/headset/gamepad.c new file mode 100644 index 000000000..969da654e --- /dev/null +++ b/src/modules/headset/gamepad.c @@ -0,0 +1,189 @@ +#include "headset/headset.h" +#include "core/os.h" +#include "core/util.h" +#include "core/maf.h" +#include +#define OS_GLFW_H +#include "core/os_glfw.h" + +#define GAMEPAD_COUNT (DEVICE_GAMEPAD_LAST - DEVICE_GAMEPAD_FIRST + 1) + +typedef struct { + bool present; + int jid; +} GamepadState; + +static struct { + bool inited; + GamepadState gamepad[GAMEPAD_COUNT]; + int gamepadsPresent; +} state; + +static GamepadState *getGamepad(Device device) { + if (device >= DEVICE_GAMEPAD_FIRST && device <= DEVICE_GAMEPAD_LAST) { + return &state.gamepad[device - DEVICE_GAMEPAD_FIRST]; + } + return NULL; +} + +void discoverGamepads() { + for(int jid = 0; jid < GLFW_JOYSTICK_LAST; jid++) { + if (glfwJoystickPresent(jid)) { + bool alreadyTracked = false; + for(int gamepadIdx = 0; gamepadIdx < GAMEPAD_COUNT; gamepadIdx++) { + GamepadState *gamepad = &state.gamepad[gamepadIdx]; + if (gamepad->present && gamepad->jid == jid) { + alreadyTracked = true; + break; + } + } + if (!alreadyTracked) { + for(int gamepadIdx = 0; gamepadIdx < GAMEPAD_COUNT; gamepadIdx++) { + GamepadState *gamepad = &state.gamepad[gamepadIdx]; + if (!gamepad->present) { + gamepad->present = true; + gamepad->jid = jid; + state.gamepadsPresent++; + printf("FOUND NEW GAMEPAD %d: %s\n", gamepadIdx, glfwGetJoystickName(jid)); + } + } + } + } + } +} + +static void refreshGamepad(int jid, int event) { + if (event == GLFW_CONNECTED) + { + for(int idx = 0; idx < GAMEPAD_COUNT; idx++) { + GamepadState *gamepad = &state.gamepad[idx]; + if (!gamepad->present) { + gamepad->present = true; + gamepad->jid = jid; + state.gamepadsPresent++; + printf("CONNECTED NEW GAMEPAD %d: %s\n", idx, glfwGetJoystickName(jid)); + } + } + } + else if (event == GLFW_DISCONNECTED) + { + for(int idx = 0; idx < GAMEPAD_COUNT; idx++) { + GamepadState *gamepad = &state.gamepad[idx]; + if (gamepad->present && gamepad->jid == jid) { + bool needRebuild = state.gamepadsPresent == GAMEPAD_COUNT; + gamepad->present = false; + state.gamepadsPresent--; + printf("DISCONNECTED GAMEPAD %d\n", idx); + if (needRebuild) + discoverGamepads(); + break; + } + } + } +} + +static void attempt_init() { + if (!state.inited && lovrPlatformHasWindow()) { + glfwSetJoystickCallback(refreshGamepad); + discoverGamepads(); + state.inited = true; + } +} + +static bool gamepad_init(float offset, uint32_t msaa) { + attempt_init(); // This is currently assumed to fail + return true; +} + +static void gamepad_destroy(void) { +} + +static bool gamepad_getPose(Device device, vec3 position, quat orientation) { + attempt_init(); + GamepadState *gamepad = getGamepad(device); + + if (!gamepad) + return false; + vec3_set(position, 0, 0, 0); + quat_set(orientation, 0, 0, 0,0); + return true; +} + +static bool gamepad_getVelocity(Device device, vec3 velocity, vec3 angularVelocity) { + return false; +} + +static bool gamepad_isDown(Device device, DeviceButton button, bool* down) { + attempt_init(); + GamepadState *gamepad = getGamepad(device); + if (!gamepad) + return false; + + GLFWgamepadstate gamepadState; + + if (!glfwGetGamepadState(gamepad->jid, &gamepadState)) + return false; + + switch (button) { + case BUTTON_TRIGGER: *down = MAX(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER], gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]) > 0.5f; return true; + case BUTTON_GRIP: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER]||gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER]; return true; + case BUTTON_THUMBSTICK: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB]; return true; + case BUTTON_MENU: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_START]; return true; + case BUTTON_A: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_A]; return true; + case BUTTON_B: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_B]; return true; + case BUTTON_X: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_X]; return true; + case BUTTON_Y: *down = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_Y]; return true; + default: return false; + } +} + +static bool gamepad_isTouched(Device device, DeviceButton button, bool* touched) { + return false; +} + +static bool gamepad_getAxis(Device device, DeviceAxis axis, float* value) { + attempt_init(); + GamepadState *gamepad = getGamepad(device); + if (!gamepad) + return false; + + GLFWgamepadstate gamepadState; + + if (!glfwGetGamepadState(gamepad->jid, &gamepadState)) + return false; + + switch (axis) { + case AXIS_TRIGGER: *value = MAX(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER], gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]); return true; + case AXIS_GRIP: *value = (gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER]||gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER])?1.0f:0.0f; return true; + case AXIS_THUMBSTICK: + value[0] = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X]; + value[1] = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]; + return true; + default: return false; + } +} + +static bool gamepad_vibrate(Device device, float strength, float duration, float frequency) { + return false; +} + +static struct ModelData* gamepad_newModelData(Device device) { + return NULL; +} + +static void gamepad_update(float dt) { +} + +HeadsetInterface lovrHeadsetGamepadDriver = { + .driverType = DRIVER_GAMEPAD, + .init = gamepad_init, + .destroy = gamepad_destroy, + .getPose = gamepad_getPose, + .getVelocity = gamepad_getVelocity, + .isDown = gamepad_isDown, + .isTouched = gamepad_isTouched, + .getAxis = gamepad_getAxis, + .vibrate = gamepad_vibrate, + .newModelData = gamepad_newModelData, + .update = gamepad_update +}; diff --git a/src/modules/headset/headset.c b/src/modules/headset/headset.c index 6b09ba733..e70485c91 100644 --- a/src/modules/headset/headset.c +++ b/src/modules/headset/headset.c @@ -35,6 +35,9 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, size_t count, float offset, uint32_ #endif #ifdef LOVR_USE_WEBVR case DRIVER_WEBVR: interface = &lovrHeadsetWebVRDriver; break; +#endif +#ifdef LOVR_USE_GAMEPAD + case DRIVER_GAMEPAD: interface = &lovrHeadsetGamepadDriver; break; #endif default: continue; } diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 9fcb275cf..bcee049b2 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -19,7 +19,8 @@ typedef enum { DRIVER_OCULUS_MOBILE, DRIVER_OPENVR, DRIVER_OPENXR, - DRIVER_WEBVR + DRIVER_WEBVR, + DRIVER_GAMEPAD, } HeadsetDriver; typedef enum { @@ -28,7 +29,11 @@ typedef enum { DEVICE_HAND_RIGHT, DEVICE_EYE_LEFT, DEVICE_EYE_RIGHT, - MAX_DEVICES + DEVICE_GAMEPAD_1, + MAX_DEVICES, + + DEVICE_GAMEPAD_FIRST = DEVICE_GAMEPAD_1, + DEVICE_GAMEPAD_LAST = DEVICE_GAMEPAD_1, } Device; typedef enum { @@ -97,6 +102,7 @@ extern HeadsetInterface lovrHeadsetWebVRDriver; extern HeadsetInterface lovrHeadsetDesktopDriver; extern HeadsetInterface lovrHeadsetOculusMobileDriver; extern HeadsetInterface lovrHeadsetLeapMotionDriver; +extern HeadsetInterface lovrHeadsetGamepadDriver; // Active drivers extern HeadsetInterface* lovrHeadsetDriver; From 44c8b3ab0a1b6b44f092deba3cd86b8112e5e9e7 Mon Sep 17 00:00:00 2001 From: mcc Date: Wed, 19 Feb 2020 17:37:30 -0500 Subject: [PATCH 02/44] Fix merge errors, roll back os_glfw.h change --- src/api/l_headset.c | 8 ++++---- src/core/os_glfw.h | 5 ----- src/modules/headset/gamepad.c | 5 +++-- src/modules/headset/headset.h | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/api/l_headset.c b/src/api/l_headset.c index 816a37926..03d922fbc 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -37,10 +37,10 @@ StringEntry Devices[] = { [DEVICE_HAND_RIGHT_POINT] = ENTRY("hand/right/point"), [DEVICE_EYE_LEFT] = ENTRY("eye/left"), [DEVICE_EYE_RIGHT] = ENTRY("eye/right"), - [DEVICE_GAMEPAD_1] = "gamepad/1", - [DEVICE_GAMEPAD_1] = "gamepad/2", - [DEVICE_GAMEPAD_1] = "gamepad/3", - [DEVICE_GAMEPAD_1] = "gamepad/4", + [DEVICE_GAMEPAD_1] = ENTRY("gamepad/1"), + [DEVICE_GAMEPAD_2] = ENTRY("gamepad/2"), + [DEVICE_GAMEPAD_3] = ENTRY("gamepad/3"), + [DEVICE_GAMEPAD_4] = ENTRY("gamepad/4"), [DEVICE_HAND_LEFT_FINGER_THUMB] = ENTRY("hand/left/finger/thumb"), [DEVICE_HAND_LEFT_FINGER_INDEX] = ENTRY("hand/left/finger/index"), [DEVICE_HAND_LEFT_FINGER_MIDDLE] = ENTRY("hand/left/finger/middle"), diff --git a/src/core/os_glfw.h b/src/core/os_glfw.h index cb81ad999..df62b6bed 100644 --- a/src/core/os_glfw.h +++ b/src/core/os_glfw.h @@ -10,9 +10,6 @@ #include #endif -#ifdef OS_GLFW_H -#define OS_GLFW_H - static struct { GLFWwindow* window; windowCloseCallback onWindowClose; @@ -255,5 +252,3 @@ HGLRC lovrPlatformGetContext() { return glfwGetWGLContext(glfwState.window); } #endif - -#endif \ No newline at end of file diff --git a/src/modules/headset/gamepad.c b/src/modules/headset/gamepad.c index 969da654e..402d83d49 100644 --- a/src/modules/headset/gamepad.c +++ b/src/modules/headset/gamepad.c @@ -3,8 +3,9 @@ #include "core/util.h" #include "core/maf.h" #include -#define OS_GLFW_H -#include "core/os_glfw.h" + +#define GLFW_INCLUDE_NONE +#include #define GAMEPAD_COUNT (DEVICE_GAMEPAD_LAST - DEVICE_GAMEPAD_FIRST + 1) diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 4a5c76d4a..4123ccf07 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -47,7 +47,7 @@ typedef enum { DEVICE_HAND_RIGHT_FINGER_MIDDLE, DEVICE_HAND_RIGHT_FINGER_RING, DEVICE_HAND_RIGHT_FINGER_PINKY, - MAX_DEVICES + MAX_DEVICES, DEVICE_GAMEPAD_FIRST = DEVICE_GAMEPAD_1, DEVICE_GAMEPAD_LAST = DEVICE_GAMEPAD_4, From 81f4232da5dfb12fc30b40fbb58fa7ee7062716a Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 15:33:41 -0800 Subject: [PATCH 03/44] opengl: Init compareMode for native textures; --- src/modules/graphics/opengl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 5b5a67a05..616a3af75 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1462,6 +1462,7 @@ Texture* lovrTextureInitFromHandle(Texture* texture, uint32_t handle, TextureTyp texture->type = type; texture->id = handle; texture->target = convertTextureTarget(type); + texture->compareMode = COMPARE_NONE; texture->native = true; int width, height; From 238496de2946a71e44d70314a2d78d65b5baf7ee Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 18 Feb 2020 17:21:40 -0800 Subject: [PATCH 04/44] Start core/gpu; --- Tupfile | 1 + config/default | 6 + src/core/gpu.h | 266 +++++++++++++++++++++++++++++ src/core/gpu_gl.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 698 insertions(+) create mode 100644 src/core/gpu.h create mode 100644 src/core/gpu_gl.c diff --git a/Tupfile b/Tupfile index 5ba4aaa62..766c1e98a 100644 --- a/Tupfile +++ b/Tupfile @@ -4,6 +4,7 @@ include_rules SRC += src/main.c SRC += src/core/arr.c SRC += src/core/fs.c +SRC_@(GPU) += src/core/gpu_@(GPU_BACKEND).c SRC += src/core/maf.c SRC += src/core/map.c SRC += src/core/os_$(PLATFORM).c diff --git a/config/default b/config/default index a1f7fd729..d1bb248f5 100644 --- a/config/default +++ b/config/default @@ -54,3 +54,9 @@ CONFIG_LEAP_PATH= ## OpenGL flavor # Can be GL, GLES, or WEBGL. Ideally this should be autodetected though. CONFIG_GL=GL + +## Experimental renderer settings +# GPU enables the core/gpu renderer +# GPU_BACKEND is used to select the backend (gl, vk, wg) +CONFIG_GPU=n +CONFIG_GPU_BACKEND=gl diff --git a/src/core/gpu.h b/src/core/gpu.h new file mode 100644 index 000000000..4a78ceea0 --- /dev/null +++ b/src/core/gpu.h @@ -0,0 +1,266 @@ +#include +#include +#include + +#pragma once + +typedef struct gpu_buffer gpu_buffer; +typedef struct gpu_texture gpu_texture; +typedef struct gpu_canvas gpu_canvas; +typedef struct gpu_shader gpu_shader; +typedef struct gpu_pipeline gpu_pipeline; + +// Buffer + +typedef enum { + GPU_BUFFER_USAGE_VERTEX = (1 << 0), + GPU_BUFFER_USAGE_INDEX = (1 << 1), + GPU_BUFFER_USAGE_UNIFORM = (1 << 2), + GPU_BUFFER_USAGE_COMPUTE = (1 << 3), + GPU_BUFFER_USAGE_COPY = (1 << 4), + GPU_BUFFER_USAGE_PASTE = (1 << 5) +} gpu_buffer_usage; + +typedef struct { + uint64_t size; + uint32_t usage; + void* data; + const char* label; +} gpu_buffer_info; + +size_t gpu_sizeof_buffer(void); +bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info); +void gpu_buffer_destroy(gpu_buffer* buffer); +uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset); +void gpu_buffer_flush(gpu_buffer* buffer, uint64_t offset, uint64_t size); +void gpu_buffer_discard(gpu_buffer* buffer); + +// Texture + +typedef enum { + GPU_TEXTURE_USAGE_SAMPLE = (1 << 0), + GPU_TEXTURE_USAGE_CANVAS = (1 << 1), + GPU_TEXTURE_USAGE_COMPUTE = (1 << 2), + GPU_TEXTURE_USAGE_COPY = (1 << 3), + GPU_TEXTURE_USAGE_PASTE = (1 << 4) +} gpu_texture_usage; + +typedef enum { + GPU_TEXTURE_TYPE_2D, + GPU_TEXTURE_TYPE_3D, + GPU_TEXTURE_TYPE_CUBE, + GPU_TEXTURE_TYPE_ARRAY +} gpu_texture_type; + +typedef enum { + GPU_TEXTURE_FORMAT_RGBA8 +} gpu_texture_format; + +typedef struct { + gpu_texture_type type; + gpu_texture_format format; + uint32_t size[3]; + uint32_t layers; + uint32_t mipmaps; + uint32_t usage; + const char* label; +} gpu_texture_info; + +size_t gpu_sizeof_texture(void); +bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info); +void gpu_texture_destroy(gpu_texture* texture); +void gpu_texture_write(gpu_texture* texture, uint8_t* data, uint16_t offset[4], uint16_t extent[4], uint16_t mip); + +// Canvas + +typedef enum { + GPU_LOAD_OP_LOAD, + GPU_LOAD_OP_CLEAR, + GPU_LOAD_OP_DISCARD +} gpu_load_op; + +typedef struct { + gpu_texture* texture; + uint32_t layer; + uint32_t level; + gpu_load_op load; + float clear[4]; +} gpu_color_attachment; + +typedef struct { + gpu_texture* texture; + gpu_load_op load; + float clear; + struct { + gpu_load_op load; + uint8_t clear; + } stencil; +} gpu_depth_attachment; + +typedef struct { + gpu_color_attachment color[4]; + gpu_depth_attachment depth; + uint32_t size[2]; + uint32_t views; + uint32_t msaa; + const char* label; +} gpu_canvas_info; + +size_t gpu_sizeof_canvas(void); +bool gpu_canvas_init(gpu_canvas* canvas, gpu_canvas_info* info); +void gpu_canvas_destroy(gpu_canvas* canvas); + +// Shader + +typedef enum { + GPU_SHADER_VERTEX, + GPU_SHADER_FRAGMENT, + GPU_SHADER_COMPUTE +} gpu_shader_stage; + +typedef struct { + gpu_shader_stage stage; + const char* source; +} gpu_shader_info; + +size_t gpu_sizeof_shader(void); +bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info); +void gpu_shader_destroy(gpu_shader* shader); + +// Pipeline + +typedef enum { + GPU_ATTR_FLOAT, + GPU_ATTR_VEC2, + GPU_ATTR_VEC3, + GPU_ATTR_VEC4 +} gpu_attribute_format; + +typedef struct { + uint8_t location; + uint8_t buffer; + uint8_t format; + uint8_t offset; +} gpu_attribute; + +typedef struct { + uint16_t stride; + uint16_t divisor; +} gpu_buffer_layout; + +typedef enum { + GPU_DRAW_POINTS, + GPU_DRAW_LINES, + GPU_DRAW_LINE_STRIP, + GPU_DRAW_TRIANGLES, + GPU_DRAW_TRIANGLE_STRIP +} gpu_draw_mode; + +typedef enum { + GPU_INDEX_U16, + GPU_INDEX_U32 +} gpu_index_stride; + +typedef enum { + GPU_CULL_NONE, + GPU_CULL_FRONT, + GPU_CULL_BACK +} gpu_cull_mode; + +typedef enum { + GPU_WINDING_CCW, + GPU_WINDING_CW +} gpu_winding; + +typedef enum { + GPU_COMPARE_NONE, + GPU_COMPARE_EQUAL, + GPU_COMPARE_NEQUAL, + GPU_COMPARE_LESS, + GPU_COMPARE_LEQUAL, + GPU_COMPARE_GREATER, + GPU_COMPARE_GEQUAL +} gpu_compare_mode; + +typedef enum { + GPU_STENCIL_KEEP, + GPU_STENCIL_ZERO, + GPU_STENCIL_REPLACE, + GPU_STENCIL_INCREMENT, + GPU_STENCIL_DECREMENT, + GPU_STENCIL_INCREMENT_WRAP, + GPU_STENCIL_DECREMENT_WRAP, + GPU_STENCIL_INVERT +} gpu_stencil_action; + +typedef struct { + gpu_stencil_action fail; + gpu_stencil_action depthFail; + gpu_stencil_action pass; + gpu_compare_mode test; + uint8_t readMask; + uint8_t writeMask; + uint8_t reference; +} gpu_stencil_state; + +typedef struct { + gpu_shader* shader; + gpu_canvas* canvas; + gpu_buffer_layout buffers[16]; + gpu_attribute attributes[16]; + gpu_index_stride indexStride; + gpu_draw_mode drawMode; + gpu_cull_mode cullMode; + gpu_winding winding; + float depthOffset; + float depthOffsetSloped; + bool depthWrite; + gpu_compare_mode depthTest; + gpu_stencil_state stencilFront; + gpu_stencil_state stencilBack; + bool alphaToCoverage; + uint8_t colorMask; + // blend +} gpu_pipeline_info; + +size_t gpu_sizeof_pipeline(void); +bool gpu_pipeline_init(gpu_pipeline* pipeline, gpu_pipeline_info* info); +void gpu_pipeline_destroy(gpu_pipeline* pipeline); + +// Entry + +typedef struct { + bool debug; + void (*getProcAddress)(const char*); +} gpu_config; + +typedef struct { + bool anisotropy; + bool dxt; +} gpu_features; + +typedef struct { + uint32_t textureSize; + uint32_t framebufferSize[2]; + uint32_t framebufferSamples; +} gpu_limits; + +typedef struct { + uint32_t drawCalls; +} gpu_stats; + +bool gpu_init(gpu_config* config); +void gpu_destroy(void); +void gpu_frame_wait(void); +void gpu_frame_finish(void); +void gpu_render_begin(gpu_canvas* canvas); +void gpu_render_finish(void); +void gpu_set_pipeline(gpu_pipeline* pipeline); +void gpu_set_vertex_buffers(gpu_buffer* buffers, uint64_t* offsets, uint32_t count); +void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset); +void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex); +void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex); +void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z); +void gpu_get_features(gpu_features* features); +void gpu_get_limits(gpu_limits* limits); +void gpu_get_stats(gpu_stats* stats); diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c new file mode 100644 index 000000000..29cc7642b --- /dev/null +++ b/src/core/gpu_gl.c @@ -0,0 +1,425 @@ +#include "gpu.h" +#include +#include + +// Loader + +#define GL_FOREACH(X)\ + X(glGetIntegerv, GLGETINTEGERV)\ + X(glEnable, GLENABLE)\ + X(glDisable, GLDISABLE)\ + X(glVertexBindingDivisor, GLVERTEXBINDINGDIVISOR)\ + X(glVertexAttribBinding, GLVERTEXATTRIBBINDING)\ + X(glVertexAttribFormat, GLVERTEXATTRIBFORMAT)\ + X(glCullFace, GLCULLFACE)\ + X(glFrontFace, GLFRONTFACE)\ + X(glPolygonOffset, GLPOLYGONOFFSET)\ + X(glDepthMask, GLDEPTHMASK)\ + X(glDepthFunc, GLDEPTHFUNC)\ + X(glColorMask, GLCOLORMASK)\ + X(glDrawArraysInstanced, GLDRAWARRAYSINSTANCED)\ + X(glDrawElementsInstancedBaseVertex, GLDRAWELEMENTSINSTANCEDBASEVERTEX)\ + X(glDispatchCompute, GLDISPATCHCOMPUTE)\ + X(glGenVertexArrays, GLGENVERTEXARRAYS)\ + X(glDeleteVertexArrays, GLDELETEVERTEXARRAYS)\ + X(glGenBuffers, GLGENBUFFERS)\ + X(glDeleteBuffers, GLDELETEBUFFERS)\ + X(glBindBuffer, GLBINDBUFFER)\ + X(glBufferStorage, GLBUFFERSTORAGE)\ + X(glMapBufferRange, GLMAPBUFFERRANGE)\ + X(glFlushMappedBufferRange, GLFLUSHMAPPEDBUFFERRANGE)\ + X(glInvalidateBufferData, GLINVALIDATEBUFFERDATA)\ + X(glGenTextures, GLGENTEXTURES)\ + X(glDeleteTextures, GLDELETETEXTURES)\ + X(glBindTexture, GLBINDTEXTURE)\ + X(glTexStorage2D, GLTEXSTORAGE2D)\ + X(glTexStorage3D, GLTEXSTORAGE3D)\ + X(glTexSubImage2D, GLTEXSUBIMAGE2D)\ + X(glTexSubImage3D, GLTEXSUBIMAGE3D)\ + X(glGenFramebuffers, GLGENFRAMEBUFFERS)\ + X(glDeleteFramebuffers, GLDELETEFRAMEBUFFERS)\ + X(glBindFramebuffer, GLBINDFRAMEBUFFER)\ + X(glFramebufferTexture2D, GLBINDFRAMEBUFFER)\ + X(glFramebufferTextureLayer, GLBINDFRAMEBUFFER)\ + X(glCheckFramebufferStatus, GLBINDFRAMEBUFFER)\ + X(glDrawBuffers, GLBINDFRAMEBUFFER)\ + X(glUseProgram, GLUSEPROGRAM)\ + +#define GL_DECLARE(fn, upper) static PFN##upper##PROC fn; +#define GL_LOAD(fn, upper) fn = (upper) config->getProcAddress(#fn); + +// Types + +struct gpu_buffer { + GLuint id; + GLenum target; + uint8_t* data; + uint64_t size; +}; + +struct gpu_texture { + GLuint id; + GLenum target; + GLenum format; + GLenum pixelFormat; + GLenum pixelType; +}; + +struct gpu_canvas { + GLuint id; +}; + +struct gpu_shader { + GLuint id; +}; + +struct gpu_pipeline { + gpu_pipeline_info info; +}; + +static struct { + GLuint vertexArray; + GLsizei bufferStrides[16]; + gpu_pipeline_info cache; + gpu_pipeline* pipeline; + gpu_canvas* canvas; +} state; + +// Setup + +GL_FOREACH(GL_DECLARE) + +bool gpu_init(gpu_config* config) { + GL_FOREACH(GL_LOAD); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_FRAMEBUFFER_SRGB); + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + glGenVertexArrays(1, &state.vertexArray); + glBindVertexArray(state.vertexArray); + return true; +} + +void gpu_destroy(void) { + glDeleteVertexArrays(1, &state.vertexArray); +} + +void gpu_frame_wait(void) { + // +} + +void gpu_frame_finish(void) { + // +} + +void gpu_render_begin(gpu_canvas* canvas) { + glBindFramebuffer(canvas->id); + state.canvas = canvas; +} + +void gpu_render_finish(void) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + state.canvas = NULL; +} + +void gpu_set_pipeline(gpu_pipeline* pipeline) { + if (state.pipeline == pipeline) { + return; + } + + gpu_pipeline_info* my = &state.cache; + gpu_pipeline_info* new = &pipeline->info; + + if (my->shader != new->shader) { + glUseProgram(new->shader->id); + my->shader = new->shader; + } + + for (uint32_t i = 0; i < 16; i++) { + if (my->buffers[i].divisor != new->buffers[i].divisor) { + glVertexBindingDivisor(i, new->buffers[i].divisor); + my->buffers[i].divisor = new->buffers[i].divisor; + } + state.bufferStrides[i] = new->buffers[i].stride; + } + + for (uint32_t i = 0; i < 16; i++) { + static const struct { GLint count; GLenum type; GLboolean normalized; } formats[] = { + [GPU_ATTR_FLOAT] = { 1, GL_FLOAT }, + [GPU_ATTR_VEC2] = { 2, GL_FLOAT }, + [GPU_ATTR_VEC3] = { 3, GL_FLOAT }, + [GPU_ATTR_VEC4] = { 4, GL_FLOAT } + }; + if (memcmp(&my->attributes[i], &new->attributes[i], sizeof(gpu_attribute))) { + glVertexAttribBinding(new->attributes[i].location, new->attributes[i].buffer); + glVertexAttribFormat( + new->attributes[i].location, + formats[new->attributes[i].format].count, + formats[new->attributes[i].format].type, + formats[new->attributes[i].format].normalized, + new->attributes[i].offset + ); + memcpy(&my->attributes[i], &new->attributes[i], sizeof(gpu_attribute)); + } + } + + my->drawMode = new->drawMode; + + if (my->cullMode != new->cullMode) { + if (new->cullMode == GPU_CULL_NONE) { + glDisable(GL_CULL_FACE); + } else { + glEnable(GL_CULL_FACE); + glCullFace(new->cullMode == GPU_CULL_FRONT ? GL_FRONT : GL_BACK); + } + my->cullMode = new->cullMode; + } + + if (my->winding != new->winding) { + glFrontFace(new->winding == GPU_WINDING_CCW ? GL_CCW : GL_CW); + my->winding = new->winding; + } + + if (my->depthOffset != new->depthOffset || my->depthOffsetSloped != new->depthOffsetSloped) { + glPolygonOffset(new->depthOffsetSloped, new->depthOffset); + my->depthOffset = new->depthOffset; + my->depthOffsetSloped = new->depthOffsetSloped; + } + + if (my->depthWrite != new->depthWrite) { + glDepthMask(new->depthWrite); + my->depthWrite = new->depthWrite; + } + + if (my->depthTest != new->depthTest) { + static const GLenum funcs[] = { + [GPU_COMPARE_EQUAL] = GL_EQUAL, + [GPU_COMPARE_NEQUAL] = GL_NOTEQUAL, + [GPU_COMPARE_LESS] = GL_LESS, + [GPU_COMPARE_LEQUAL] = GL_LEQUAL, + [GPU_COMPARE_GREATER] = GL_GREATER, + [GPU_COMPARE_GEQUAL] = GL_GEQUAL + }; + if (new->depthTest == GPU_COMPARE_NONE) { + glDisable(GL_DEPTH_TEST); + } else { + if (my->depthTest == GPU_COMPARE_NONE) { + glEnable(GL_DEPTH_TEST); + } + glDepthFunc(funcs[new->depthTest]); + } + my->depthTest = new->depthTest; + } + + // stencil + + if (my->alphaToCoverage != new->alphaToCoverage) { + if (new->alphaToCoverage) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + my->alphaToCoverage = new->alphaToCoverage; + } + + if (my->colorMask != new->colorMask) { + glColorMask(new->colorMask & 0x8, new->colorMask & 0x4, new->colorMask & 0x2, new->colorMask & 0x1); + my->colorMask != new->colorMask; + } + + // blend + + state.pipeline = pipeline; +} + +void gpu_set_vertex_buffers(gpu_buffer* buffers, uint64_t* offsets, uint32_t count) { + // +} + +void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->id); +} + +static const GLenum drawModes[] = { + [GPU_DRAW_POINTS] = GL_POINTS, + [GPU_DRAW_LINES] = GL_LINES, + [GPU_DRAW_LINE_STRIP] = GL_LINE_STRIP, + [GPU_DRAW_TRIANGLES] = GL_TRIANGLES, + [GPU_DRAW_TRIANGLE_STRIP] = GL_TRIANGLE_STRIP +}; + +void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex) { + GLenum mode = drawModes[state.pipeline->drawMode]; + glDrawArraysInstanced(mode, firstVertex, vertexCount, instanceCount); +} + +void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex) { + GLenum mode = drawModes[state.pipeline->drawMode]; + GLenum type = state.pipeline->indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + GLvoid* offset = (GLvoid*) (firstIndex * (type == GL_UNSIGNED_SHORT ? 2 : 4)); + glDrawElementsInstancedBaseVertex(mode, indexCount, type, offset, instanceCount, baseVertex); +} + +void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z) { + glUseProgram(shader->id); + glDispatchCompute(x, y, z); +} + +void gpu_get_features(gpu_features* features) { + features->anisotropy = true; + features->dxt = true; +} + +void gpu_get_limits(gpu_limits* limits) { + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &limits->textureSize); + glGetIntegerv(GL_MAX_FRAMEBUFFER_WIDTH, &limits->framebufferSize[0]); + glGetIntegerv(GL_MAX_FRAMEBUFFER_HEIGHT, &limits->framebufferSize[1]); + glGetIntegerv(GL_MAX_FRAMEBUFFER_SAMPLES, &limits->framebufferSamples); +} + +void gpu_get_stats(gpu_stats* stats) { + stats->drawCalls = 0; +} + +// Buffer + +size_t gpu_sizeof_buffer(void) { + return sizeof(gpu_buffer); +} + +bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) { + if (info->usage & GPU_BUFFER_USAGE_VERTEX) buffer->target = GL_ARRAY_BUFFER; + else if (info->usage & GPU_BUFFER_USAGE_INDEX) buffer->target = GL_ELEMENT_ARRAY_BUFFER; + else if (info->usage & GPU_BUFFER_USAGE_UNIFORM) buffer->target = GL_UNIFORM_BUFFER; + else if (info->usage & GPU_BUFFER_USAGE_COMPUTE) buffer->target = GL_SHADER_STORAGE_BUFFER; + else if (info->usage & GPU_BUFFER_USAGE_COPY) buffer->target = GL_COPY_READ_BUFFER; + else if (info->usage & GPU_BUFFER_USAGE_PASTE) buffer->target = GL_COPY_WRITE_BUFFER; + else buffer->target = GL_TRANSFORM_FEEDBACK_BUFFER; // Haha no one uses this r-right + glGenBuffers(1, &buffer->id); + glBindBuffer(buffer->target, buffer->id); + GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; + glBufferStorage(buffer->target, info->size, info->data, flags); + GLbitfield access = flags | GL_MAP_FLUSH_EXPLICIT_BIT; + buffer->data = glMapBufferRange(buffer->target, 0, info->size, access); + buffer->size = info->size; + return true; +} + +void gpu_buffer_destroy(gpu_buffer* buffer) { + glDeleteBuffers(1, &buffer->id); +} + +uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset) { + return buffer->data + offset; +} + +void gpu_buffer_flush(gpu_buffer* buffer, uint64_t offset, uint64_t size) { + glBindBuffer(buffer->target, buffer->id); + glFlushMappedBufferRange(buffer->target, offset, size); +} + +void gpu_buffer_discard(gpu_buffer* buffer) { + glBindBuffer(buffer->target, buffer->id); + glUnmapBuffer(buffer->target); + glInvalidateBufferData(buffer->target); + GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + buffer->data = glMapBufferRange(buffer->target, 0, buffer->size, flags); +} + +// Texture + +size_t gpu_sizeof_texture(void) { + return sizeof(gpu_texture); +} + +bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { + static const GLenum targets[] = { + [GPU_TEXTURE_TYPE_2D] = GL_TEXTURE_2D, + [GPU_TEXTURE_TYPE_3D] = GL_TEXTURE_3D, + [GPU_TEXTURE_TYPE_CUBE] = GL_TEXTURE_CUBE_MAP, + [GPU_TEXTURE_TYPE_ARRAY] = GL_TEXTURE_2D_ARRAY + }; + static const struct { GLenum format, pixelFormat, pixelType; } formats[] = { + [GPU_TEXTURE_FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE } + }; + texture->target = targets[info->type]; + texture->format = formats[info->format].format; + texture->pixelFormat = formats[info->format].pixelFormat; + texture->pixelType = formats[info->format].pixelType; + glGenTextures(1, &texture->id); + glBindTexture(texture->target, texture->id); + if (info->type == GPU_TEXTURE_TYPE_2D || info->type == GPU_TEXTURE_TYPE_CUBE) { + glTexStorage2D(texture->target, info->mipmaps, texture->format, info->size[0], info->size[1]); + } else { + uint32_t depth = info->type == GPU_TEXTURE_TYPE_ARRAY ? info->layers : info->size[2]; + glTexStorage3D(texture->target, info->mipmaps, texture->format, info->size[0], info->size[1], depth); + } + return true; +} + +void gpu_texture_destroy(gpu_texture* texture) { + glDeleteTextures(1, &texture->id); +} + +void gpu_texture_write(gpu_texture* texture, uint8_t* data, uint16_t offset[4], uint16_t extent[4], uint16_t mip) { + glBindTexture(texture->target, texture->id); + int x = offset[0], y = offset[1], z = offset[2], i = offset[3]; + int w = extent[0], h = extent[1], d = extent[2], n = extent[3]; + GLenum format = texture->pixelFormat; + GLenum type = texture->pixelType; + switch (texture->target) { + case GL_TEXTURE_2D: glTexSubImage2D(GL_TEXTURE_2D, mip, x, y, w, h, format, type, data); + case GL_TEXTURE_3D: glTexSubImage3D(GL_TEXTURE_3D, mip, x, y, z, w, h, d, format, type, data); + case GL_TEXTURE_CUBE: glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + z, mip, x, y, w, h, format, type, data); + case GL_TEXTURE_ARRAY: glTexSubImage3D(GL_TEXTURE_2D_ARRAY, mip, x, y, i, w, h, n, format, type, data); + default: break; + } +} + +// Canvas + +size_t gpu_sizeof_canvas(void) { + return sizeof(gpu_canvas); +} + +bool gpu_canvas_init(gpu_canvas* canvas, gpu_canvas_info* info) { + glGenFramebuffers(1, &canvas->id); + glBindFramebuffer(GL_FRAMEBUFFER, canvas->id); + uint32_t bufferCount = 0; + GLenum buffers[4] = { GL_NONE }; + for (uint32_t i = 0; i < 4 && info->color[i].texture; i++, bufferCount++) { + buffers[i] = GL_COLOR_ATTACHMENT0 + i; + gpu_color_attachment* attachment = &info->color[i]; + switch (attachment->texture->target) { + case GL_TEXTURE_2D: glFramebufferTexture2D(GL_FRAMEBUFFER, buffers[i], GL_TEXTURE_2D, attachment->texture->id, attachment->level); break; + case GL_TEXTURE_3D: glFramebufferTextureLayer(GL_FRAMEBUFFER, buffers[i], attachment->texture->id, attachment->level, attachment->layer); break; + case GL_TEXTURE_CUBE_MAP: glFramebufferTexture2D(GL_FRAMEBUFFER, buffers[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X + attachment->layer, attachment->texture->id, attachment->level); break; + case GL_TEXTURE_2D_ARRAY: glFramebufferTextureLayer(GL_FRAMEBUFFER, buffers[i], attachment->texture->id, attachment->level, attachment->layer); break; + } + } + glDrawBuffers(bufferCount, buffers); + if (info->depth.texture) { + GLenum attachment = GL_DEPTH_STENCIL_ATTACHMENT; // Depends on texture format + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, info->depth.texture->id, 0); + } + return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; +} + +void gpu_canvas_destroy(gpu_canvas* canvas) { + glDeleteFramebuffers(1, &canvas->id); +} + +// Shader + +/* */ + +// Pipeline + +bool gpu_pipeline_init(gpu_pipeline* pipeline, gpu_pipeline_info* info) { + pipeline->info = *info; +} + +void gpu_pipeline_destroy(gpu_pipeline* pipeline) { + // +} From cb4a7a5a431476041f266548c20ef2630d1d3a75 Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 17:42:33 -0800 Subject: [PATCH 05/44] core/gpu: indirect drawing commands; --- src/core/gpu.h | 2 ++ src/core/gpu_gl.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/core/gpu.h b/src/core/gpu.h index 4a78ceea0..a0621796c 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -260,6 +260,8 @@ void gpu_set_vertex_buffers(gpu_buffer* buffers, uint64_t* offsets, uint32_t cou void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset); void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex); void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex); +void gpu_draw_indirect(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount); +void gpu_draw_indirect_indexed(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount); void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z); void gpu_get_features(gpu_features* features); void gpu_get_limits(gpu_limits* limits); diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 29cc7642b..2ff02026e 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -261,6 +261,19 @@ void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firs glDrawElementsInstancedBaseVertex(mode, indexCount, type, offset, instanceCount, baseVertex); } +void gpu_draw_indirect(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { + GLenum mode = drawModes[state.pipeline->drawMode]; + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->id); + glMultiDrawArraysIndirect(mode, (GLvoid*) offset, drawCount, 0); +} + +void gpu_draw_indirect_indexed(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { + GLenum mode = drawModes[state.pipeline->drawMode]; + GLenum type = state.pipeline->indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->id); + glMultiDrawElementsIndirect(mode, type, (GLvoid*) offset, drawCount, 0); +} + void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z) { glUseProgram(shader->id); glDispatchCompute(x, y, z); From 0620cade9eaba66258cb5045b2627e8e08e45cb7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 18:01:38 -0800 Subject: [PATCH 06/44] core/gpu: gpu_set_vertex_buffers; fixes; --- src/core/gpu.h | 5 +++-- src/core/gpu_gl.c | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index a0621796c..3b4470cd0 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -221,6 +221,7 @@ typedef struct { bool alphaToCoverage; uint8_t colorMask; // blend + const char* label; } gpu_pipeline_info; size_t gpu_sizeof_pipeline(void); @@ -231,7 +232,7 @@ void gpu_pipeline_destroy(gpu_pipeline* pipeline); typedef struct { bool debug; - void (*getProcAddress)(const char*); + void* (*getProcAddress)(const char*); } gpu_config; typedef struct { @@ -256,7 +257,7 @@ void gpu_frame_finish(void); void gpu_render_begin(gpu_canvas* canvas); void gpu_render_finish(void); void gpu_set_pipeline(gpu_pipeline* pipeline); -void gpu_set_vertex_buffers(gpu_buffer* buffers, uint64_t* offsets, uint32_t count); +void gpu_set_vertex_buffers(gpu_buffer** buffers, uint64_t* offsets, uint32_t count); void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset); void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex); void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex); diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 2ff02026e..d2a8dde80 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -11,6 +11,7 @@ X(glVertexBindingDivisor, GLVERTEXBINDINGDIVISOR)\ X(glVertexAttribBinding, GLVERTEXATTRIBBINDING)\ X(glVertexAttribFormat, GLVERTEXATTRIBFORMAT)\ + X(glBindVertexBuffer, GLBINDVERTEXBUFFER)\ X(glCullFace, GLCULLFACE)\ X(glFrontFace, GLFRONTFACE)\ X(glPolygonOffset, GLPOLYGONOFFSET)\ @@ -22,6 +23,7 @@ X(glDispatchCompute, GLDISPATCHCOMPUTE)\ X(glGenVertexArrays, GLGENVERTEXARRAYS)\ X(glDeleteVertexArrays, GLDELETEVERTEXARRAYS)\ + X(glBindVertexArray, GLBINDVERTEXARRAY)\ X(glGenBuffers, GLGENBUFFERS)\ X(glDeleteBuffers, GLDELETEBUFFERS)\ X(glBindBuffer, GLBINDBUFFER)\ @@ -141,7 +143,6 @@ void gpu_set_pipeline(gpu_pipeline* pipeline) { glVertexBindingDivisor(i, new->buffers[i].divisor); my->buffers[i].divisor = new->buffers[i].divisor; } - state.bufferStrides[i] = new->buffers[i].stride; } for (uint32_t i = 0; i < 16; i++) { @@ -233,8 +234,10 @@ void gpu_set_pipeline(gpu_pipeline* pipeline) { state.pipeline = pipeline; } -void gpu_set_vertex_buffers(gpu_buffer* buffers, uint64_t* offsets, uint32_t count) { - // +void gpu_set_vertex_buffers(gpu_buffer** buffers, uint64_t* offsets, uint32_t count) { + for (uint32_t i = 0; i < count; i++) { // Not convinced multibind would be any better here + glBindVertexBuffer(i, buffers[i] ? buffers[i]->id : 0, offsets[i], state.bufferStrides[i]); + } } void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset) { From aadadb968bdf7e82826861e7a1206d4f295f1e2f Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 18:33:59 -0800 Subject: [PATCH 07/44] core/gpu: will it blend; --- src/core/gpu.h | 32 +++++++++++++++++++++++++++++++- src/core/gpu_gl.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index 3b4470cd0..71809fdad 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -203,6 +203,36 @@ typedef struct { uint8_t reference; } gpu_stencil_state; +typedef enum { + GPU_BLEND_ZERO, + GPU_BLEND_ONE, + GPU_BLEND_SRC_COLOR, + GPU_BLEND_ONE_MINUS_SRC_COLOR, + GPU_BLEND_SRC_ALPHA, + GPU_BLEND_ONE_MINUS_SRC_ALPHA, + GPU_BLEND_DST_COLOR, + GPU_BLEND_ONE_MINUS_DST_COLOR, + GPU_BLEND_DST_ALPHA, + GPU_BLEND_ONE_MINUS_DST_ALPHA +} gpu_blend_factor; + +typedef enum { + GPU_BLEND_ADD, + GPU_BLEND_SUB, + GPU_BLEND_RSUB, + GPU_BLEND_MIN, + GPU_BLEND_MAX +} gpu_blend_op; + +typedef struct { + struct { + gpu_blend_factor src; + gpu_blend_factor dst; + gpu_blend_op op; + } color, alpha; + bool enabled; +} gpu_blend_state; + typedef struct { gpu_shader* shader; gpu_canvas* canvas; @@ -220,7 +250,7 @@ typedef struct { gpu_stencil_state stencilBack; bool alphaToCoverage; uint8_t colorMask; - // blend + gpu_blend_state blend; const char* label; } gpu_pipeline_info; diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index d2a8dde80..215e57fe8 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -18,6 +18,8 @@ X(glDepthMask, GLDEPTHMASK)\ X(glDepthFunc, GLDEPTHFUNC)\ X(glColorMask, GLCOLORMASK)\ + X(glBlendFuncSeparate, GLBLENDFUNCSEPARATE)\ + X(glBlendEquationSeparate, GLBLENDEQUATIONSEPARATE)\ X(glDrawArraysInstanced, GLDRAWARRAYSINSTANCED)\ X(glDrawElementsInstancedBaseVertex, GLDRAWELEMENTSINSTANCEDBASEVERTEX)\ X(glDispatchCompute, GLDISPATCHCOMPUTE)\ @@ -229,7 +231,39 @@ void gpu_set_pipeline(gpu_pipeline* pipeline) { my->colorMask != new->colorMask; } - // blend + if (my->blend.enabled != new->blend.enabled) { + if (new->blend.enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + my->blend.enabled = new->blend.enabled; + } + + if (memcmp(&my->blend, &new->blend, sizeof(my->blend))) { + static const GLenum factors[] = { + [GPU_BLEND_ZERO] = GL_ZERO, + [GPU_BLEND_ONE] = GL_ONE, + [GPU_BLEND_SRC_COLOR] = GL_SRC_COLOR, + [GPU_BLEND_ONE_MINUS_SRC_COLOR] = GL_ONE_MINUS_SRC_COLOR, + [GPU_BLEND_SRC_ALPHA] = GL_SRC_ALPHA, + [GPU_BLEND_ONE_MINUS_SRC_ALPHA] = GL_ONE_MINUS_SRC_ALPHA, + [GPU_BLEND_DST_COLOR] = GL_DST_COLOR, + [GPU_BLEND_ONE_MINUS_DST_COLOR] = GL_ONE_MINUS_DST_COLOR, + [GPU_BLEND_DST_ALPHA] = GL_DST_ALPHA, + [GPU_BLEND_ONE_MINUS_DST_ALPHA] = GL_ONE_MINUS_DST_ALPHA + }; + static const GLenum opps[] = { + [GPU_BLEND_ADD] = GL_FUNC_ADD, + [GPU_BLEND_SUB] = GL_FUNC_SUBTRACT, + [GPU_BLEND_RSUB] = GL_FUNC_REVERSE_SUBTRACT, + [GPU_BLEND_MIN] = GL_MIN, + [GPU_BLEND_MAX] = GL_MAX + }; + glBlendFuncSeparate(factors[new->blend.color.src], factors[new->blend.color.dst], factors[new->blend.alpha.src], factors[new->blend.alpha.dst]); + glBlendEquationSeparate(opps[new->blend.color.op], opps[new->blend.alpha.op]); + memcpy(&my->blend, &new->blend, sizeof(my->blend)); + } state.pipeline = pipeline; } From a658da78ad60f3778299c3130ce8486bd97c95f4 Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 19:36:30 -0800 Subject: [PATCH 08/44] core/gpu: Add all uncompressed texture formats; --- src/core/gpu.h | 14 +++++++++++++- src/core/gpu_gl.c | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index 71809fdad..2cf03c997 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -53,7 +53,19 @@ typedef enum { } gpu_texture_type; typedef enum { - GPU_TEXTURE_FORMAT_RGBA8 + GPU_TEXTURE_FORMAT_RGBA8, + GPU_TEXTURE_FORMAT_RGBA4, + GPU_TEXTURE_FORMAT_R16F, + GPU_TEXTURE_FORMAT_RG16F, + GPU_TEXTURE_FORMAT_RGBA16F, + GPU_TEXTURE_FORMAT_R32F, + GPU_TEXTURE_FORMAT_RG32F, + GPU_TEXTURE_FORMAT_RGBA32F, + GPU_TEXTURE_FORMAT_RGB10A2, + GPU_TEXTURE_FORMAT_RG11B10F, + GPU_TEXTURE_FORMAT_D16, + GPU_TEXTURE_FORMAT_D32F, + GPU_TEXTURE_FORMAT_D24S8 } gpu_texture_format; typedef struct { diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 215e57fe8..b79f4633e 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -391,7 +391,19 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { [GPU_TEXTURE_TYPE_ARRAY] = GL_TEXTURE_2D_ARRAY }; static const struct { GLenum format, pixelFormat, pixelType; } formats[] = { - [GPU_TEXTURE_FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE } + [GPU_TEXTURE_FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, + [GPU_TEXTURE_FORMAT_RGBA4] = { GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE }, + [GPU_TEXTURE_FORMAT_R16F] = { GL_R16F, GL_RED, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_RG16F] = { GL_RG16F, GL_RG, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_RGBA16F] = { GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_R32F] = { GL_R32F, GL_RED, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RG32F] = { GL_RG32F, GL_RG, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RGBA32F] = { GL_RGBA32F, GL_RGBA, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RGB10A2] = { GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, + [GPU_TEXTURE_FORMAT_RG11B10F] = { GL_R11F_G11F_B10F, GL_RGBA, GL_UNSIGNED_INT_10F_11F_11F_REV }, + [GPU_TEXTURE_FORMAT_D16] = { GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, + [GPU_TEXTURE_FORMAT_D32F] = { GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_D24S8] = { GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 } }; texture->target = targets[info->type]; texture->format = formats[info->format].format; From 6452f516802583266daf149e1c8e94e6d52bf184 Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 19 Feb 2020 20:28:30 -0800 Subject: [PATCH 09/44] core/gpu: attribute formats; --- src/core/gpu.h | 24 ++++++++++++++++++++---- src/core/gpu_gl.c | 42 ++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index 2cf03c997..cbe377374 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -142,10 +142,26 @@ void gpu_shader_destroy(gpu_shader* shader); // Pipeline typedef enum { - GPU_ATTR_FLOAT, - GPU_ATTR_VEC2, - GPU_ATTR_VEC3, - GPU_ATTR_VEC4 + GPU_FLOAT_F32, + GPU_VEC2_F32, + GPU_VEC2_F16, + GPU_VEC2_U16N, + GPU_VEC2_I16N, + GPU_VEC3_F32, + GPU_VEC4_F32, + GPU_VEC4_F16, + GPU_VEC4_U16N, + GPU_VEC4_I16N, + GPU_VEC4_U8N, + GPU_VEC4_I8N, + GPU_UINT_U32, + GPU_UVEC2_U32, + GPU_UVEC3_U32, + GPU_UVEC4_U32, + GPU_INT_I32, + GPU_IVEC2_I32, + GPU_IVEC3_I32, + GPU_IVEC4_I32 } gpu_attribute_format; typedef struct { diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index b79f4633e..0f6810d9b 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -149,20 +149,38 @@ void gpu_set_pipeline(gpu_pipeline* pipeline) { for (uint32_t i = 0; i < 16; i++) { static const struct { GLint count; GLenum type; GLboolean normalized; } formats[] = { - [GPU_ATTR_FLOAT] = { 1, GL_FLOAT }, - [GPU_ATTR_VEC2] = { 2, GL_FLOAT }, - [GPU_ATTR_VEC3] = { 3, GL_FLOAT }, - [GPU_ATTR_VEC4] = { 4, GL_FLOAT } + [GPU_FLOAT_F32] = { 1, GL_FLOAT, GL_FALSE }, + [GPU_VEC2_F32] = { 2, GL_FLOAT, GL_FALSE }, + [GPU_VEC2_U16N] = { 2, GL_UNSIGNED_SHORT, GL_TRUE }, + [GPU_VEC2_I16N] = { 2, GL_SHORT, GL_TRUE }, + [GPU_VEC3_F32] = { 3, GL_FLOAT, GL_FALSE }, + [GPU_VEC4_F32] = { 4, GL_FLOAT, GL_FALSE }, + [GPU_VEC4_F16] = { 4, GL_HALF_FLOAT, GL_FALSE }, + [GPU_VEC4_U16N] = { 4, GL_UNSIGNED_SHORT, GL_TRUE }, + [GPU_VEC4_I16N] = { 4, GL_SHORT, GL_TRUE }, + [GPU_VEC4_U8N] = { 4, GL_UNSIGNED_BYTE, GL_TRUE }, + [GPU_VEC4_I8N] = { 4, GL_BYTE, GL_TRUE }, + [GPU_UINT_U32] = { 1, GL_UNSIGNED_INT }, + [GPU_UVEC2_U32] = { 2, GL_UNSIGNED_INT }, + [GPU_UVEC3_U32] = { 3, GL_UNSIGNED_INT }, + [GPU_UVEC4_U32] = { 4, GL_UNSIGNED_INT }, + [GPU_INT_I32] = { 1, GL_INT }, + [GPU_IVEC2_I32] = { 2, GL_INT }, + [GPU_IVEC3_I32] = { 3, GL_INT }, + [GPU_IVEC4_I32] = { 4, GL_INT } }; if (memcmp(&my->attributes[i], &new->attributes[i], sizeof(gpu_attribute))) { - glVertexAttribBinding(new->attributes[i].location, new->attributes[i].buffer); - glVertexAttribFormat( - new->attributes[i].location, - formats[new->attributes[i].format].count, - formats[new->attributes[i].format].type, - formats[new->attributes[i].format].normalized, - new->attributes[i].offset - ); + GLuint location = new->attributes[i].location; + GLint size = formats[new->attributes[i].format].count; + GLenum type = formats[new->attributes[i].format].type; + GLboolean normalized = formats[new->attributes[i].format].normalized; + GLuint offset = new->attributes[i].offset; + glVertexAttribBinding(location, new->attributes[i].buffer); + if (new->attributes[i].format >= GPU_UINT_U32) { + glVertexAttribIFormat(location, size, type, offset); + } else { + glVertexAttribFormat(location, size, type, normalized, offset); + } memcpy(&my->attributes[i], &new->attributes[i], sizeof(gpu_attribute)); } } From 7d2bec7283cd325ad43c888529e2d5902441e536 Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 20 Feb 2020 12:23:31 -0800 Subject: [PATCH 10/44] core/gpu: add vulkan backend; --- src/core/gpu.h | 13 +- src/core/gpu_vk.c | 1324 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1334 insertions(+), 3 deletions(-) create mode 100644 src/core/gpu_vk.c diff --git a/src/core/gpu.h b/src/core/gpu.h index cbe377374..22601cee4 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -31,7 +31,7 @@ typedef struct { size_t gpu_sizeof_buffer(void); bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info); void gpu_buffer_destroy(gpu_buffer* buffer); -uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset); +uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset, uint64_t size); void gpu_buffer_flush(gpu_buffer* buffer, uint64_t offset, uint64_t size); void gpu_buffer_discard(gpu_buffer* buffer); @@ -131,8 +131,13 @@ typedef enum { } gpu_shader_stage; typedef struct { - gpu_shader_stage stage; - const char* source; + const void* code; + size_t size; +} gpu_shader_source; + +typedef struct { + gpu_shader_source vertex, fragment, compute; + const char* label; } gpu_shader_info; size_t gpu_sizeof_shader(void); @@ -291,6 +296,8 @@ void gpu_pipeline_destroy(gpu_pipeline* pipeline); typedef struct { bool debug; void* (*getProcAddress)(const char*); + void (*callback)(void* context, const char* message, int level); + void* context; } gpu_config; typedef struct { diff --git a/src/core/gpu_vk.c b/src/core/gpu_vk.c new file mode 100644 index 000000000..50726a73d --- /dev/null +++ b/src/core/gpu_vk.c @@ -0,0 +1,1324 @@ +#include "gpu.h" +#include +#include +#include +#define VK_NO_PROTOTYPES +#include + +#define COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define SCRATCHPAD_SIZE (16 * 1024 * 1024) +#define VOIDP_TO_U64(x) (((union { uint64_t u; void* p; }) { .p = x }).u) +#define GPU_THROW(s) if (state.config.callback) { state.config.callback(state.config.context, s, true); } +#define GPU_CHECK(c, s) if (!(c)) { GPU_THROW(s); } +#define GPU_VK(f) do { VkResult r = (f); GPU_CHECK(r >= 0, getErrorString(r)); } while (0) + +// Functions that don't require an instance +#define GPU_FOREACH_ANONYMOUS(X)\ + X(vkCreateInstance) + +// Functions that require an instance but don't require a device +#define GPU_FOREACH_INSTANCE(X)\ + X(vkDestroyInstance)\ + X(vkCreateDebugUtilsMessengerEXT)\ + X(vkDestroyDebugUtilsMessengerEXT)\ + X(vkEnumeratePhysicalDevices)\ + X(vkGetPhysicalDeviceProperties)\ + X(vkGetPhysicalDeviceMemoryProperties)\ + X(vkGetPhysicalDeviceQueueFamilyProperties)\ + X(vkCreateDevice)\ + X(vkDestroyDevice)\ + X(vkGetDeviceQueue)\ + X(vkGetDeviceProcAddr) + +// Functions that require a device +#define GPU_FOREACH_DEVICE(X)\ + X(vkSetDebugUtilsObjectNameEXT)\ + X(vkQueueSubmit)\ + X(vkDeviceWaitIdle)\ + X(vkCreateCommandPool)\ + X(vkDestroyCommandPool)\ + X(vkAllocateCommandBuffers)\ + X(vkFreeCommandBuffers)\ + X(vkBeginCommandBuffer)\ + X(vkEndCommandBuffer)\ + X(vkCreateFence)\ + X(vkDestroyFence)\ + X(vkWaitForFences)\ + X(vkResetFences)\ + X(vkCmdPipelineBarrier)\ + X(vkCreateBuffer)\ + X(vkDestroyBuffer)\ + X(vkGetBufferMemoryRequirements)\ + X(vkBindBufferMemory)\ + X(vkCmdCopyBuffer)\ + X(vkCreateImage)\ + X(vkDestroyImage)\ + X(vkGetImageMemoryRequirements)\ + X(vkBindImageMemory)\ + X(vkCmdCopyBufferToImage)\ + X(vkAllocateMemory)\ + X(vkFreeMemory)\ + X(vkMapMemory)\ + X(vkUnmapMemory)\ + X(vkCreateSampler)\ + X(vkDestroySampler)\ + X(vkCreateRenderPass)\ + X(vkDestroyRenderPass)\ + X(vkCmdBeginRenderPass)\ + X(vkCmdEndRenderPass)\ + X(vkCreateImageView)\ + X(vkDestroyImageView)\ + X(vkCreateFramebuffer)\ + X(vkDestroyFramebuffer)\ + X(vkCreateShaderModule)\ + X(vkDestroyShaderModule)\ + X(vkCreateGraphicsPipelines)\ + X(vkDestroyPipeline)\ + X(vkCmdBindPipeline)\ + X(vkCmdBindVertexBuffers)\ + X(vkCmdBindIndexBuffer)\ + X(vkCmdDraw)\ + X(vkCmdDrawIndexed)\ + X(vkCmdDrawIndirect)\ + X(vkCmdDrawIndexedIndirect)\ + X(vkCmdDispatch) + +// Used to load/declare Vulkan functions without lots of clutter +#define GPU_LOAD_ANONYMOUS(fn) fn = (PFN_##fn) vkGetInstanceProcAddr(NULL, #fn); +#define GPU_LOAD_INSTANCE(fn) fn = (PFN_##fn) vkGetInstanceProcAddr(state.instance, #fn); +#define GPU_LOAD_DEVICE(fn) fn = (PFN_##fn) vkGetDeviceProcAddr(state.device, #fn); +#define GPU_DECLARE(fn) static PFN_##fn fn; + +// Declare function pointers +GPU_FOREACH_ANONYMOUS(GPU_DECLARE) +GPU_FOREACH_INSTANCE(GPU_DECLARE) +GPU_FOREACH_DEVICE(GPU_DECLARE) + +typedef struct { + uint16_t frame; + uint16_t scratchpad; + uint32_t offset; +} gpu_mapping; + +struct gpu_buffer { + VkBuffer handle; + VkDeviceMemory memory; + gpu_mapping mapping; + uint64_t targetOffset; + uint64_t targetSize; +}; + +struct gpu_texture { + VkImage handle; + VkImageView view; + VkImageLayout layout; + VkDeviceMemory memory; + VkImageAspectFlagBits aspect; + gpu_texture* source; + gpu_texture_type type; + VkFormat format; +}; + +struct gpu_sampler { + VkSampler handle; +}; + +struct gpu_canvas { + VkRenderPass handle; + VkFramebuffer framebuffer; + VkRect2D renderArea; + VkViewport viewport; + uint32_t colorAttachmentCount; + struct { + gpu_texture* texture; + VkImageLayout layout; + VkPipelineStageFlags stage; + VkAccessFlags access; + } sync[5]; + VkClearValue clears[5]; +}; + +struct gpu_shader { + VkShaderModule handles[2]; + VkPipelineShaderStageCreateInfo pipelineInfo[2]; + VkDescriptorSetLayout layouts[4]; + VkPipelineLayout pipelineLayout; +}; + +struct gpu_pipeline { + VkPipeline handle; + VkIndexType indexType; +}; + +typedef struct { + VkObjectType type; + void* handle; +} gpu_ref; + +typedef struct { + gpu_ref* data; + size_t capacity; + size_t length; +} gpu_freelist; + +typedef struct { + VkBuffer buffer; + VkDeviceMemory memory; + void* data; +} gpu_scratchpad; + +typedef struct { + gpu_scratchpad* list; + uint16_t count; + uint16_t current; + uint32_t cursor; +} gpu_pool; + +typedef struct { + uint32_t index; + VkFence fence; + VkCommandBuffer commandBuffer; + gpu_freelist freelist; + gpu_pool pool; +} gpu_frame; + +static struct { + gpu_config config; + void* library; + VkInstance instance; + VkDebugUtilsMessengerEXT messenger; + VkPhysicalDeviceMemoryProperties memoryProperties; + VkMemoryRequirements scratchpadMemoryRequirements; + uint32_t scratchpadMemoryType; + uint32_t queueFamilyIndex; + VkDevice device; + VkQueue queue; + VkCommandPool commandPool; + gpu_frame frames[2]; + gpu_frame* frame; + gpu_pipeline* pipeline; +} state; + +static void condemn(void* handle, VkObjectType type); +static void purge(gpu_frame* frame); +static bool loadShader(gpu_shader_source* source, VkShaderStageFlagBits stage, VkShaderModule* handle, VkPipelineShaderStageCreateInfo* pipelineInfo); +static void setLayout(gpu_texture* texture, VkImageLayout layout, VkPipelineStageFlags nextStages, VkAccessFlags nextActions); +static void nickname(void* object, VkObjectType type, const char* name); +static VkBool32 debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, const VkDebugUtilsMessengerCallbackDataEXT* data, void* context); +static const char* getErrorString(VkResult result); + +static uint8_t* gpu_map(uint64_t size, gpu_mapping* mapping) { + gpu_pool* pool = &state.frame->pool; + gpu_scratchpad* scratchpad = &pool->list[pool->current]; + + if (pool->count == 0 || pool->cursor + size > SCRATCHPAD_SIZE) { + GPU_CHECK(size <= SCRATCHPAD_SIZE, "Tried to map too much memory"); + + pool->cursor = 0; + pool->current = pool->count++; + pool->list = realloc(pool->list, pool->count * sizeof(gpu_scratchpad)); + GPU_CHECK(pool->list, "Out of memory"); + scratchpad = &pool->list[pool->current]; + + // Create buffer + VkBufferCreateInfo bufferInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = SCRATCHPAD_SIZE, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT + }; + + GPU_VK(vkCreateBuffer(state.device, &bufferInfo, NULL, &scratchpad->buffer)); + + // Get memory requirements, once + if (state.scratchpadMemoryRequirements.size == 0) { + state.scratchpadMemoryType = ~0u; + vkGetBufferMemoryRequirements(state.device, scratchpad->buffer, &state.scratchpadMemoryRequirements); + for (uint32_t i = 0; i < state.memoryProperties.memoryTypeCount; i++) { + uint32_t flags = state.memoryProperties.memoryTypes[i].propertyFlags; + uint32_t mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + if ((flags & mask) == mask && (state.scratchpadMemoryRequirements.memoryTypeBits & (1 << i))) { + state.scratchpadMemoryType = i; + break; + } + } + + if (state.scratchpadMemoryType == ~0u) { + vkDestroyBuffer(state.device, scratchpad->buffer, NULL); + return NULL; // PANIC + } + } + + // Allocate, bind + VkMemoryAllocateInfo memoryInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = state.scratchpadMemoryRequirements.size, + .memoryTypeIndex = state.scratchpadMemoryType + }; + + if (vkAllocateMemory(state.device, &memoryInfo, NULL, &scratchpad->memory)) { + vkDestroyBuffer(state.device, scratchpad->buffer, NULL); + GPU_THROW("Out of memory"); + } + + if (vkBindBufferMemory(state.device, scratchpad->buffer, scratchpad->memory, 0)) { + vkDestroyBuffer(state.device, scratchpad->buffer, NULL); + vkFreeMemory(state.device, scratchpad->memory, NULL); + GPU_THROW("Out of memory"); + } + + if (vkMapMemory(state.device, scratchpad->memory, 0, VK_WHOLE_SIZE, 0, &scratchpad->data)) { + vkDestroyBuffer(state.device, scratchpad->buffer, NULL); + vkFreeMemory(state.device, scratchpad->memory, NULL); + GPU_THROW("Out of memory"); + } + } + + mapping->frame = state.frame->index; + mapping->offset = pool->cursor; + mapping->scratchpad = pool->current; + return (uint8_t*) scratchpad->data + pool->cursor; +} + +bool gpu_init(gpu_config* config) { + state.config = *config; + + // Load + state.library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); // TODO cross platform + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(state.library, "vkGetInstanceProcAddr"); + GPU_FOREACH_ANONYMOUS(GPU_LOAD_ANONYMOUS); + + { // Instance + const char* layers[] = { + "VK_LAYER_LUNARG_object_tracker", + "VK_LAYER_LUNARG_core_validation", + "VK_LAYER_LUNARG_parameter_validation" + }; + + const char* extensions[] = { + "VK_EXT_debug_utils" + }; + + VkInstanceCreateInfo instanceInfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &(VkApplicationInfo) { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .apiVersion = VK_MAKE_VERSION(1, 1, 0) + }, + .enabledLayerCount = state.config.debug ? COUNTOF(layers) : 0, + .ppEnabledLayerNames = layers, + .enabledExtensionCount = state.config.debug ? COUNTOF(extensions) : 0, + .ppEnabledExtensionNames = extensions + }; + + if (vkCreateInstance(&instanceInfo, NULL, &state.instance)) { + gpu_destroy(); + return false; + } + + GPU_FOREACH_INSTANCE(GPU_LOAD_INSTANCE); + + if (state.config.debug) { + VkDebugUtilsMessengerCreateInfoEXT messengerInfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = debugCallback, + .pUserData = state.config.context + }; + + if (vkCreateDebugUtilsMessengerEXT(state.instance, &messengerInfo, NULL, &state.messenger)) { + gpu_destroy(); + return false; + } + } + } + + { // Device + uint32_t deviceCount = 1; + VkPhysicalDevice physicalDevice; + if (vkEnumeratePhysicalDevices(state.instance, &deviceCount, &physicalDevice) < 0) { + gpu_destroy(); + return false; + } + + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + + state.queueFamilyIndex = ~0u; + VkQueueFamilyProperties queueFamilies[4]; + uint32_t queueFamilyCount = COUNTOF(queueFamilies); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies); + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + uint32_t flags = queueFamilies[i].queueFlags; + if ((flags & VK_QUEUE_GRAPHICS_BIT) && (flags & VK_QUEUE_COMPUTE_BIT)) { + state.queueFamilyIndex = i; + break; + } + } + + if (state.queueFamilyIndex == ~0u) { + gpu_destroy(); + return false; + } + + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &state.memoryProperties); + + VkDeviceQueueCreateInfo queueInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = state.queueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &(float) { 1.f } + }; + + VkDeviceCreateInfo deviceInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queueInfo + }; + + if (vkCreateDevice(physicalDevice, &deviceInfo, NULL, &state.device)) { + gpu_destroy(); + return false; + } + + vkGetDeviceQueue(state.device, state.queueFamilyIndex, 0, &state.queue); + + GPU_FOREACH_DEVICE(GPU_LOAD_DEVICE); + + VkCommandPoolCreateInfo commandPoolInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = state.queueFamilyIndex + }; + + if (vkCreateCommandPool(state.device, &commandPoolInfo, NULL, &state.commandPool)) { + gpu_destroy(); + return false; + } + } + + { // Frame state + state.frame = &state.frames[0]; + + for (uint32_t i = 0; i < COUNTOF(state.frames); i++) { + state.frames[i].index = i; + + VkCommandBufferAllocateInfo commandBufferInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = state.commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + if (vkAllocateCommandBuffers(state.device, &commandBufferInfo, &state.frames[i].commandBuffer)) { + gpu_destroy(); + return false; + } + + VkFenceCreateInfo fenceInfo = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT + }; + + if (vkCreateFence(state.device, &fenceInfo, NULL, &state.frames[i].fence)) { + gpu_destroy(); + return false; + } + } + } + + return true; +} + +void gpu_destroy(void) { + if (state.device) vkDeviceWaitIdle(state.device); + for (uint32_t i = 0; i < COUNTOF(state.frames); i++) { + gpu_frame* frame = &state.frames[i]; + + purge(frame); + free(frame->freelist.data); + + if (frame->fence) vkDestroyFence(state.device, frame->fence, NULL); + if (frame->commandBuffer) vkFreeCommandBuffers(state.device, state.commandPool, 1, &frame->commandBuffer); + + for (uint32_t j = 0; j < frame->pool.count; j++) { + gpu_scratchpad* scratchpad = &frame->pool.list[j]; + vkDestroyBuffer(state.device, scratchpad->buffer, NULL); + vkUnmapMemory(state.device, scratchpad->memory); + vkFreeMemory(state.device, scratchpad->memory, NULL); + } + + free(frame->pool.list); + } + if (state.commandPool) vkDestroyCommandPool(state.device, state.commandPool, NULL); + if (state.device) vkDestroyDevice(state.device, NULL); + if (state.messenger) vkDestroyDebugUtilsMessengerEXT(state.instance, state.messenger, NULL); + if (state.instance) vkDestroyInstance(state.instance, NULL); + dlclose(state.library); + memset(&state, 0, sizeof(state)); +} + +void gpu_frame_wait(void) { + GPU_VK(vkWaitForFences(state.device, 1, &state.frame->fence, VK_FALSE, ~0ull)); + GPU_VK(vkResetFences(state.device, 1, &state.frame->fence)); + state.frame->pool.current = 0; + state.frame->pool.cursor = 0; + purge(state.frame); + + VkCommandBufferBeginInfo beginfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + + GPU_VK(vkBeginCommandBuffer(state.frame->commandBuffer, &beginfo)); +} + +void gpu_frame_finish(void) { + GPU_VK(vkEndCommandBuffer(state.frame->commandBuffer)); + + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pCommandBuffers = &state.frame->commandBuffer, + .commandBufferCount = 1 + }; + + GPU_VK(vkQueueSubmit(state.queue, 1, &submitInfo, state.frame->fence)); + + state.frame = &state.frames[(state.frame->index + 1) % COUNTOF(state.frames)]; +} + +void gpu_render_begin(gpu_canvas* canvas) { + for (uint32_t i = 0; i < COUNTOF(canvas->sync) && canvas->sync[i].texture; i++) { + setLayout(canvas->sync[i].texture, canvas->sync[i].layout, canvas->sync[i].stage, canvas->sync[i].access); + } + + VkRenderPassBeginInfo beginfo = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = canvas->handle, + .framebuffer = canvas->framebuffer, + .renderArea = canvas->renderArea, + .clearValueCount = COUNTOF(canvas->clears), + .pClearValues = canvas->clears + }; + + vkCmdBeginRenderPass(state.frame->commandBuffer, &beginfo, VK_SUBPASS_CONTENTS_INLINE); +} + +void gpu_render_finish(void) { + vkCmdEndRenderPass(state.frame->commandBuffer); +} + +void gpu_set_pipeline(gpu_pipeline* pipeline) { + if (state.pipeline != pipeline) { + vkCmdBindPipeline(state.frame->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); + state.pipeline = pipeline; + } +} + +void gpu_set_vertex_buffers(gpu_buffer** buffers, uint64_t* offsets, uint32_t count) { + VkBuffer handles[16]; + for (uint32_t i = 0; i < count; i++) { + handles[i] = buffers[i]->handle; + } + vkCmdBindVertexBuffers(state.frame->commandBuffer, 0, count, handles, offsets); +} + +void gpu_set_index_buffer(gpu_buffer* buffer, uint64_t offset) { + vkCmdBindIndexBuffer(state.frame->commandBuffer, buffer->handle, offset, state.pipeline->indexType); +} + +void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex) { + vkCmdDraw(state.frame->commandBuffer, vertexCount, instanceCount, firstVertex, 0); +} + +void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex) { + vkCmdDrawIndexed(state.frame->commandBuffer, indexCount, instanceCount, firstIndex, baseVertex, 0); +} + +void gpu_draw_indirect(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { + vkCmdDrawIndirect(state.frame->commandBuffer, buffer->handle, offset, drawCount, 0); +} + +void gpu_draw_indirect_indexed(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { + vkCmdDrawIndexedIndirect(state.frame->commandBuffer, buffer->handle, offset, drawCount, 0); +} + +void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z) { + // TODO bind compute pipeline + vkCmdDispatch(state.frame->commandBuffer, x, y, z); +} + +void gpu_get_features(gpu_features* features) { + // +} + +void gpu_get_limits(gpu_limits* limits) { + // +} + +void gpu_get_stats(gpu_stats* stats) { + // +} + +// Buffer + +size_t gpu_sizeof_buffer() { + return sizeof(gpu_buffer); +} + +bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) { + memset(buffer, 0, sizeof(*buffer)); + + VkBufferUsageFlags usage = + ((info->usage & GPU_BUFFER_USAGE_VERTEX) ? VK_BUFFER_USAGE_VERTEX_BUFFER_BIT : 0) | + ((info->usage & GPU_BUFFER_USAGE_INDEX) ? VK_BUFFER_USAGE_INDEX_BUFFER_BIT : 0) | + ((info->usage & GPU_BUFFER_USAGE_UNIFORM) ? VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT : 0) | + ((info->usage & GPU_BUFFER_USAGE_COMPUTE) ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : 0) | + ((info->usage & GPU_BUFFER_USAGE_COPY) ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0) | + ((info->usage & GPU_BUFFER_USAGE_PASTE) ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0); + + VkBufferCreateInfo bufferInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = info->size, + .usage = usage + }; + + if (vkCreateBuffer(state.device, &bufferInfo, NULL, &buffer->handle)) { + return false; + } + + nickname(buffer->handle, VK_OBJECT_TYPE_BUFFER, info->label); + + VkMemoryRequirements requirements; + vkGetBufferMemoryRequirements(state.device, buffer->handle, &requirements); + + uint32_t memoryType = ~0u; + for (uint32_t i = 0; i < state.memoryProperties.memoryTypeCount; i++) { + uint32_t flags = state.memoryProperties.memoryTypes[i].propertyFlags; + uint32_t mask = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if ((flags & mask) == mask && (requirements.memoryTypeBits & (1 << i))) { + memoryType = i; + break; + } + } + + if (memoryType == ~0u) { + vkDestroyBuffer(state.device, buffer->handle, NULL); + return false; + } + + VkMemoryAllocateInfo memoryInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = requirements.size, + .memoryTypeIndex = memoryType + }; + + if (vkAllocateMemory(state.device, &memoryInfo, NULL, &buffer->memory)) { + vkDestroyBuffer(state.device, buffer->handle, NULL); + return false; + } + + if (vkBindBufferMemory(state.device, buffer->handle, buffer->memory, 0)) { + vkDestroyBuffer(state.device, buffer->handle, NULL); + vkFreeMemory(state.device, buffer->memory, NULL); + return false; + } + + return true; +} + +void gpu_buffer_destroy(gpu_buffer* buffer) { + if (buffer->handle) condemn(buffer->handle, VK_OBJECT_TYPE_BUFFER); + if (buffer->memory) condemn(buffer->memory, VK_OBJECT_TYPE_DEVICE_MEMORY); + memset(buffer, 0, sizeof(*buffer)); +} + +uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset, uint64_t size) { + buffer->targetOffset = offset; + buffer->targetSize = size; + return gpu_map(size, &buffer->mapping); +} + +void gpu_buffer_unmap(gpu_buffer* buffer) { + if (buffer->mapping.frame != state.frame->index) { + return; + } + + VkBuffer source = state.frame->pool.list[buffer->mapping.scratchpad].buffer; + VkBuffer destination = buffer->handle; + + VkBufferCopy copy = { + .srcOffset = buffer->mapping.offset, + .dstOffset = buffer->targetOffset, + .size = buffer->targetSize + }; + + vkCmdCopyBuffer(state.frame->commandBuffer, source, destination, 1, ©); +} + +// Texture + +static const struct { VkFormat format; VkImageAspectFlagBits aspect; } textureInfo[] = { + [GPU_TEXTURE_FORMAT_RGBA8] = { VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RGBA4] = { VK_FORMAT_R4G4B4A4_UNORM_PACK16, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_R16F] = { VK_FORMAT_R16_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RG16F] = { VK_FORMAT_R16G16_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RGBA16F] = { VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_R32F] = { VK_FORMAT_R32_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RG32F] = { VK_FORMAT_R32G32_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RGBA32F] = { VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RGB10A2] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_RG11B10F] = { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_IMAGE_ASPECT_COLOR_BIT }, + [GPU_TEXTURE_FORMAT_D16] = { VK_FORMAT_D16_UNORM, VK_IMAGE_ASPECT_DEPTH_BIT }, + [GPU_TEXTURE_FORMAT_D32F] = { VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT }, + [GPU_TEXTURE_FORMAT_D24S8] = { VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT } +}; + +size_t gpu_sizeof_texture() { + return sizeof(gpu_texture); +} + +bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { + memset(texture, 0, sizeof(*texture)); + + VkImageType type; + VkImageCreateFlags flags = 0; + switch (info->type) { + case GPU_TEXTURE_TYPE_2D: type = VK_IMAGE_TYPE_2D; break; + case GPU_TEXTURE_TYPE_3D: type = VK_IMAGE_TYPE_3D; break; + case GPU_TEXTURE_TYPE_CUBE: type = VK_IMAGE_TYPE_2D; flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; break; + case GPU_TEXTURE_TYPE_ARRAY: type = VK_IMAGE_TYPE_3D; flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; break; + default: return false; + } + + texture->format = textureInfo[info->format].format; + texture->aspect = textureInfo[info->format].aspect; + + bool depth = texture->aspect & VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageUsageFlags usage = + (((info->usage & GPU_TEXTURE_USAGE_CANVAS) && !depth) ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) | + (((info->usage & GPU_TEXTURE_USAGE_CANVAS) && depth) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0) | + ((info->usage & GPU_TEXTURE_USAGE_SAMPLE) ? VK_IMAGE_USAGE_SAMPLED_BIT : 0) | + ((info->usage & GPU_TEXTURE_USAGE_COMPUTE) ? VK_IMAGE_USAGE_STORAGE_BIT : 0) | + ((info->usage & GPU_TEXTURE_USAGE_COPY) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0) | + ((info->usage & GPU_TEXTURE_USAGE_PASTE) ? VK_IMAGE_USAGE_TRANSFER_DST_BIT : 0); + + VkImageCreateInfo imageInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = flags, + .imageType = type, + .format = texture->format, + .extent.width = info->size[0], + .extent.height = info->size[1], + .extent.depth = info->size[2], + .mipLevels = info->mipmaps, + .arrayLayers = info->layers, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = usage + }; + + if (vkCreateImage(state.device, &imageInfo, NULL, &texture->handle)) { + return false; + } + + nickname(texture->handle, VK_OBJECT_TYPE_IMAGE, info->label); + + VkMemoryRequirements requirements; + vkGetImageMemoryRequirements(state.device, texture->handle, &requirements); + uint32_t memoryType = ~0u; + for (uint32_t i = 0; i < state.memoryProperties.memoryTypeCount; i++) { + uint32_t flags = state.memoryProperties.memoryTypes[i].propertyFlags; + uint32_t mask = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if ((flags & mask) == mask && (requirements.memoryTypeBits & (1 << i))) { + memoryType = i; + break; + } + } + + if (memoryType == ~0u) { + vkDestroyImage(state.device, texture->handle, NULL); + return false; + } + + VkMemoryAllocateInfo memoryInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = requirements.size, + .memoryTypeIndex = memoryType + }; + + if (vkAllocateMemory(state.device, &memoryInfo, NULL, &texture->memory)) { + vkDestroyImage(state.device, texture->handle, NULL); + return false; + } + + if (vkBindImageMemory(state.device, texture->handle, texture->memory, 0)) { + vkDestroyImage(state.device, texture->handle, NULL); + vkFreeMemory(state.device, texture->memory, NULL); + return false; + } + + texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; + + /* + if (!gpu_texture_init_view(texture, texture, NULL)) { + vkDestroyImage(state.device, texture->handle, NULL); + vkFreeMemory(state.device, texture->memory, NULL); + return false; + } + */ + + return true; +} + +/* +bool gpu_texture_init_view(gpu_texture* texture, gpu_texture* source, gpu_texture_view_info* info) { + if (texture != source) { + texture->handle = VK_NULL_HANDLE; + texture->memory = VK_NULL_HANDLE; + texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; + texture->source = source; + texture->type = info ? info->type : source->type; + texture->format = (info && info->format) ? convertTextureFormat(info->format) : source->format; + texture->aspect = source->aspect; + } + + VkImageViewType type; + switch (texture->type) { + case GPU_TEXTURE_TYPE_2D: type = VK_IMAGE_VIEW_TYPE_2D; break; + case GPU_TEXTURE_TYPE_3D: type = VK_IMAGE_VIEW_TYPE_3D; break; + case GPU_TEXTURE_TYPE_CUBE: type = VK_IMAGE_VIEW_TYPE_CUBE; break; + case GPU_TEXTURE_TYPE_ARRAY: type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; + default: return false; + } + + VkImageViewCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = source->handle, + .viewType = type, + .format = texture->format, + .subresourceRange = { + .aspectMask = texture->aspect, + .baseMipLevel = info ? info->baseMipmap : 0, + .levelCount = (info && info->mipmapCount) ? info->mipmapCount : VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = info ? info->baseLayer : 0, + .layerCount = (info && info->layerCount) ? info->layerCount : VK_REMAINING_ARRAY_LAYERS + } + }; + + GPU_VK(vkCreateImageView(state.device, &createInfo, NULL, &texture->view)); + + return true; +} +*/ + +void gpu_texture_destroy(gpu_texture* texture) { + if (texture->handle) condemn(texture->handle, VK_OBJECT_TYPE_IMAGE); + if (texture->memory) condemn(texture->memory, VK_OBJECT_TYPE_DEVICE_MEMORY); + if (texture->view) condemn(texture->view, VK_OBJECT_TYPE_IMAGE_VIEW); + memset(texture, 0, sizeof(*texture)); +} + +void gpu_texture_paste(gpu_texture* texture, uint8_t* data, uint64_t size, uint16_t offset[4], uint16_t extent[4], uint16_t mip) { + if (size > SCRATCHPAD_SIZE) { + // TODO loop or big boi staging buffer + return; + } + + gpu_mapping mapping; + uint8_t* scratch = gpu_map(size, &mapping); + memcpy(data, scratch, size); + + VkBuffer source = state.frames[mapping.frame].pool.list[mapping.scratchpad].buffer; + VkImage destination = texture->handle; + + VkBufferImageCopy copy = { + .bufferOffset = mapping.offset, + .imageSubresource.aspectMask = texture->aspect, + .imageSubresource.mipLevel = mip, + .imageSubresource.baseArrayLayer = offset[3], + .imageSubresource.layerCount = extent[3], + .imageOffset = { offset[0], offset[1], offset[2] }, + .imageExtent = { extent[0], extent[1], extent[2] } + }; + + setLayout(texture, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); + + vkCmdCopyBufferToImage(state.frame->commandBuffer, source, destination, texture->layout, 1, ©); +} + +// Sampler + +/* +size_t gpu_sizeof_sampler() { + return sizeof(gpu_sampler); +} + +bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info) { + VkSamplerCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = info->mag == GPU_FILTER_NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR, + .minFilter = info->min == GPU_FILTER_NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR, + .mipmapMode = info->mip == GPU_FILTER_NEAREST ? VK_SAMPLER_MIPMAP_MODE_NEAREST : VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = convertWrap(info->wrapu), + .addressModeV = convertWrap(info->wrapv), + .addressModeW = convertWrap(info->wrapw), + .anisotropyEnable = info->anisotropy > 0.f, + .maxAnisotropy = info->anisotropy + }; + + GPU_VK(vkCreateSampler(state.device, &createInfo, NULL, &sampler->handle)); + + nickname(sampler, VK_OBJECT_TYPE_SAMPLER, info->label); + + return true; +} + +void gpu_sampler_destroy(gpu_sampler* sampler) { + if (sampler->handle) condemn(sampler->handle, VK_OBJECT_TYPE_SAMPLER); + memset(sampler, 0, sizeof(*sampler)); +} +*/ + +// Canvas + +size_t gpu_sizeof_canvas() { + return sizeof(gpu_canvas); +} + +bool gpu_canvas_init(gpu_canvas* canvas, gpu_canvas_info* info) { + static const VkAttachmentLoadOp loadOps[] = { + [GPU_LOAD_OP_LOAD] = VK_ATTACHMENT_LOAD_OP_LOAD, + [GPU_LOAD_OP_CLEAR] = VK_ATTACHMENT_LOAD_OP_CLEAR, + [GPU_LOAD_OP_DISCARD] = VK_ATTACHMENT_LOAD_OP_DONT_CARE + }; + + VkAttachmentDescription attachments[5]; + VkAttachmentReference references[5]; + VkImageView imageViews[5]; + canvas->colorAttachmentCount = 0; + uint32_t totalCount = 0; + + for (uint32_t i = 0; i < COUNTOF(info->color) && info->color[i].texture; i++, canvas->colorAttachmentCount++, totalCount++) { + attachments[i] = (VkAttachmentDescription) { + .format = info->color[i].texture->format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = loadOps[info->color[i].load], + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + references[i] = (VkAttachmentReference) { i, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + imageViews[i] = info->color[i].texture->view; + + memcpy(canvas->clears[i].color.float32, info->color[i].clear, 4 * sizeof(float)); + canvas->sync[i].texture = info->color[i].texture;; + canvas->sync[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + canvas->sync[i].stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + canvas->sync[i].access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + canvas->sync[i].access |= info->color[i].load ? VK_ACCESS_COLOR_ATTACHMENT_READ_BIT : VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (info->depth.texture) { + uint32_t i = totalCount++; + + attachments[i] = (VkAttachmentDescription) { + .format = info->depth.texture->format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = loadOps[info->depth.load], + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = loadOps[info->depth.stencil.load], + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL + }; + + references[i] = (VkAttachmentReference) { i, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + imageViews[i] = info->depth.texture->view; + + canvas->clears[i].depthStencil.depth = info->depth.clear; + canvas->clears[i].depthStencil.stencil = info->depth.stencil.clear; + canvas->sync[i].texture = info->depth.texture; + canvas->sync[i].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + canvas->sync[i].stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + canvas->sync[i].access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + canvas->sync[i].access |= (info->depth.load || info->depth.stencil.load) ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT : 0; + } + + VkSubpassDescription subpass = { + .colorAttachmentCount = canvas->colorAttachmentCount, + .pColorAttachments = references, + .pDepthStencilAttachment = info->depth.texture ? &references[canvas->colorAttachmentCount] : NULL + }; + + VkRenderPassCreateInfo renderPassInfo = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = totalCount, + .pAttachments = attachments, + .subpassCount = 1, + .pSubpasses = &subpass + }; + + if (vkCreateRenderPass(state.device, &renderPassInfo, NULL, &canvas->handle)) { + return false; + } + + canvas->renderArea = (VkRect2D) { { 0, 0 }, { info->size[0], info->size[1] } }; + canvas->viewport = (VkViewport) { 0.f, 0.f, info->size[0], info->size[1], 0.f, 1.f }; + + VkFramebufferCreateInfo framebufferInfo = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = canvas->handle, + .attachmentCount = totalCount, + .pAttachments = imageViews, + .width = canvas->renderArea.extent.width, + .height = canvas->renderArea.extent.height, + .layers = 1 + }; + + if (vkCreateFramebuffer(state.device, &framebufferInfo, NULL, &canvas->framebuffer)) { + vkDestroyRenderPass(state.device, canvas->handle, NULL); + } + + nickname(canvas, VK_OBJECT_TYPE_RENDER_PASS, info->label); + + return false; +} + +void gpu_canvas_destroy(gpu_canvas* canvas) { + if (canvas->handle) condemn(canvas->handle, VK_OBJECT_TYPE_RENDER_PASS); + if (canvas->framebuffer) condemn(canvas->framebuffer, VK_OBJECT_TYPE_FRAMEBUFFER); + memset(canvas, 0, sizeof(*canvas)); +} + +// Shader + +size_t gpu_sizeof_shader() { + return sizeof(gpu_shader); +} + +bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) { + memset(shader, 0, sizeof(*shader)); + + if (info->compute.code) { + loadShader(&info->compute, VK_SHADER_STAGE_COMPUTE_BIT, &shader->handles[0], &shader->pipelineInfo[0]); + } else { + loadShader(&info->vertex, VK_SHADER_STAGE_VERTEX_BIT, &shader->handles[0], &shader->pipelineInfo[0]); + loadShader(&info->fragment, VK_SHADER_STAGE_FRAGMENT_BIT, &shader->handles[1], &shader->pipelineInfo[1]); + } + + return true; +} + +void gpu_shader_destroy(gpu_shader* shader) { + if (shader->handles[0]) condemn(shader->handles[0], VK_OBJECT_TYPE_SHADER_MODULE); + if (shader->handles[1]) condemn(shader->handles[1], VK_OBJECT_TYPE_SHADER_MODULE); + memset(shader, 0, sizeof(*shader)); +} + +// Pipeline + +bool gpu_pipeline_init(gpu_pipeline* pipeline, gpu_pipeline_info* info) { + static const VkPrimitiveTopology topologies[] = { + [GPU_DRAW_POINTS] = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + [GPU_DRAW_LINES] = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + [GPU_DRAW_LINE_STRIP] = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + [GPU_DRAW_TRIANGLES] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + [GPU_DRAW_TRIANGLE_STRIP] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP + }; + + static const VkCullModeFlagBits cullModes[] = { + [GPU_CULL_NONE] = VK_CULL_MODE_NONE, + [GPU_CULL_FRONT] = VK_CULL_MODE_FRONT_BIT, + [GPU_CULL_BACK] = VK_CULL_MODE_BACK_BIT + }; + + static const VkFrontFace frontFaces[] = { + [GPU_WINDING_CCW] = VK_FRONT_FACE_COUNTER_CLOCKWISE, + [GPU_WINDING_CW] = VK_FRONT_FACE_CLOCKWISE + }; + + static const VkCompareOp compareOps[] = { + [GPU_COMPARE_NONE] = VK_COMPARE_OP_ALWAYS, + [GPU_COMPARE_EQUAL] = VK_COMPARE_OP_EQUAL, + [GPU_COMPARE_NEQUAL] = VK_COMPARE_OP_NOT_EQUAL, + [GPU_COMPARE_LESS] = VK_COMPARE_OP_LESS, + [GPU_COMPARE_LEQUAL] = VK_COMPARE_OP_LESS_OR_EQUAL, + [GPU_COMPARE_GREATER] = VK_COMPARE_OP_GREATER, + [GPU_COMPARE_GEQUAL] = VK_COMPARE_OP_GREATER_OR_EQUAL + }; + + static const VkStencilOp stencilOps[] = { + [GPU_STENCIL_KEEP] = VK_STENCIL_OP_KEEP, + [GPU_STENCIL_ZERO] = VK_STENCIL_OP_ZERO, + [GPU_STENCIL_REPLACE] = VK_STENCIL_OP_REPLACE, + [GPU_STENCIL_INCREMENT] = VK_STENCIL_OP_INCREMENT_AND_CLAMP, + [GPU_STENCIL_DECREMENT] = VK_STENCIL_OP_DECREMENT_AND_CLAMP, + [GPU_STENCIL_INCREMENT_WRAP] = VK_STENCIL_OP_INCREMENT_AND_WRAP, + [GPU_STENCIL_DECREMENT_WRAP] = VK_STENCIL_OP_DECREMENT_AND_WRAP, + [GPU_STENCIL_INVERT] = VK_STENCIL_OP_INVERT + }; + + static const VkBlendFactor blendFactors[] = { + [GPU_BLEND_ZERO] = VK_BLEND_FACTOR_ZERO, + [GPU_BLEND_ONE] = VK_BLEND_FACTOR_ONE, + [GPU_BLEND_SRC_COLOR] = VK_BLEND_FACTOR_SRC_COLOR, + [GPU_BLEND_ONE_MINUS_SRC_COLOR] = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + [GPU_BLEND_SRC_ALPHA] = VK_BLEND_FACTOR_SRC_ALPHA, + [GPU_BLEND_ONE_MINUS_SRC_ALPHA] = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + [GPU_BLEND_DST_COLOR] = VK_BLEND_FACTOR_DST_COLOR, + [GPU_BLEND_ONE_MINUS_DST_COLOR] = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, + [GPU_BLEND_DST_ALPHA] = VK_BLEND_FACTOR_DST_ALPHA, + [GPU_BLEND_ONE_MINUS_DST_ALPHA] = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA + }; + + static const VkBlendOp blendOps[] = { + [GPU_BLEND_ADD] = VK_BLEND_OP_ADD, + [GPU_BLEND_SUB] = VK_BLEND_OP_SUBTRACT, + [GPU_BLEND_RSUB] = VK_BLEND_OP_REVERSE_SUBTRACT, + [GPU_BLEND_MIN] = VK_BLEND_OP_MIN, + [GPU_BLEND_MAX] = VK_BLEND_OP_MAX + }; + + static const VkIndexType types[] = { + [GPU_INDEX_U16] = VK_INDEX_TYPE_UINT16, + [GPU_INDEX_U32] = VK_INDEX_TYPE_UINT32 + }; + + VkPipelineVertexInputStateCreateInfo vertexInput = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 0, + .pVertexBindingDescriptions = NULL, + .vertexAttributeDescriptionCount = 0, + .pVertexAttributeDescriptions = NULL + }; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = topologies[info->drawMode], + .primitiveRestartEnable = true + }; + + VkPipelineViewportStateCreateInfo viewport = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .pViewports = &info->canvas->viewport + }; + + VkPipelineRasterizationStateCreateInfo rasterization = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .cullMode = cullModes[info->cullMode], + .frontFace = frontFaces[info->winding], + .depthBiasEnable = info->depthOffset != 0.f, + .depthBiasConstantFactor = info->depthOffset, + .depthBiasSlopeFactor = info->depthOffsetSloped, + .lineWidth = 1.f + }; + + VkPipelineMultisampleStateCreateInfo multisample = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .alphaToCoverageEnable = info->alphaToCoverage + }; + + VkPipelineDepthStencilStateCreateInfo depthStencil = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = info->depthTest != GPU_COMPARE_NONE, + .depthWriteEnable = info->depthWrite, + .depthCompareOp = compareOps[info->depthTest], + .stencilTestEnable = info->stencilFront.test != GPU_COMPARE_NONE || info->stencilBack.test != GPU_COMPARE_NONE, + .front = { + .failOp = stencilOps[info->stencilFront.fail], + .passOp = stencilOps[info->stencilFront.pass], + .depthFailOp = stencilOps[info->stencilFront.depthFail], + .compareOp = compareOps[info->stencilFront.test], + .compareMask = info->stencilFront.readMask, + .writeMask = info->stencilFront.writeMask, + .reference = info->stencilFront.reference + }, + .back = { + .failOp = stencilOps[info->stencilBack.fail], + .passOp = stencilOps[info->stencilBack.pass], + .depthFailOp = stencilOps[info->stencilBack.depthFail], + .compareOp = compareOps[info->stencilBack.test], + .compareMask = info->stencilBack.readMask, + .writeMask = info->stencilBack.writeMask, + .reference = info->stencilBack.reference + } + }; + + VkPipelineColorBlendAttachmentState blendState = { + .blendEnable = info->blend.enabled, + .srcColorBlendFactor = blendFactors[info->blend.color.src], + .dstColorBlendFactor = blendFactors[info->blend.color.dst], + .colorBlendOp = blendOps[info->blend.color.op], + .srcAlphaBlendFactor = blendFactors[info->blend.alpha.src], + .dstAlphaBlendFactor = blendFactors[info->blend.alpha.dst], + .alphaBlendOp = blendOps[info->blend.alpha.op], + .colorWriteMask = info->colorMask + }; + + VkPipelineColorBlendStateCreateInfo colorBlend = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .attachmentCount = info->canvas->colorAttachmentCount, + .pAttachments = (VkPipelineColorBlendAttachmentState[4]) { blendState, blendState, blendState, blendState } + }; + + VkGraphicsPipelineCreateInfo pipelineInfo = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 2, + .pStages = info->shader->pipelineInfo, + .pVertexInputState = &vertexInput, + .pInputAssemblyState = &inputAssembly, + .pViewportState = &viewport, + .pRasterizationState = &rasterization, + .pMultisampleState = &multisample, + .pDepthStencilState = &depthStencil, + .pColorBlendState = &colorBlend, + .renderPass = info->canvas->handle + }; + + if (vkCreateGraphicsPipelines(state.device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &pipeline->handle)) { + return false; + } + + nickname(pipeline, VK_OBJECT_TYPE_PIPELINE, info->label); + pipeline->indexType = types[info->indexStride]; + return true; +} + +void gpu_pipeline_destroy(gpu_pipeline* pipeline) { + if (pipeline->handle) condemn(pipeline->handle, VK_OBJECT_TYPE_PIPELINE); +} + +// Helpers + +// Condemns an object, marking it for deletion (objects can't be destroyed while the GPU is still +// using them). Condemned objects are purged in gpu_begin_frame after waiting on a fence. We have +// to manage the freelist memory ourselves because the user could immediately free memory after +// destroying a resource. +// TODO currently there is a bug where condemning a resource outside of begin/end frame will +// immediately purge it on the next call to begin_frame (it should somehow get added to the previous +// frame's freelist, ugh, maybe advance frame index in begin_frame instead of end_frame) +// TODO even though this is fairly lightweight it might be worth doing some object tracking to see +// if you actually need to delay the destruction, and try to destroy it immediately when possible. +// Because Lua objects are GC'd we probably already have our own delay and could get away with +// skipping the freelist a lot of the time. Also you might need to track object access anyway for +// barriers, so this could wombo combo with the condemnation. It might be complicated though if you +// have to track access across multiple frames. +static void condemn(void* handle, VkObjectType type) { + gpu_freelist* freelist = &state.frame->freelist; + + if (freelist->length >= freelist->capacity) { + freelist->capacity = freelist->capacity ? (freelist->capacity * 2) : 1; + freelist->data = realloc(freelist->data, freelist->capacity * sizeof(*freelist->data)); + GPU_CHECK(freelist->data, "Out of memory"); + } + + freelist->data[freelist->length++] = (gpu_ref) { type, handle }; +} + +static void purge(gpu_frame* frame) { + for (size_t i = 0; i < frame->freelist.length; i++) { + gpu_ref* ref = &frame->freelist.data[i]; + switch (ref->type) { + case VK_OBJECT_TYPE_BUFFER: vkDestroyBuffer(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_IMAGE: vkDestroyImage(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_DEVICE_MEMORY: vkFreeMemory(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_IMAGE_VIEW: vkDestroyImageView(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_SAMPLER: vkDestroySampler(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_RENDER_PASS: vkDestroyRenderPass(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_FRAMEBUFFER: vkDestroyFramebuffer(state.device, ref->handle, NULL); break; + case VK_OBJECT_TYPE_PIPELINE: vkDestroyPipeline(state.device, ref->handle, NULL); break; + default: GPU_THROW("Unreachable"); break; + } + } + frame->freelist.length = 0; +} + +static bool loadShader(gpu_shader_source* source, VkShaderStageFlagBits stage, VkShaderModule* handle, VkPipelineShaderStageCreateInfo* pipelineInfo) { + VkShaderModuleCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = source->size, + .pCode = source->code + }; + + if (vkCreateShaderModule(state.device, &info, NULL, handle)) { + return false; + } + + *pipelineInfo = (VkPipelineShaderStageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = stage, + .module = *handle, + .pName = "main", + .pSpecializationInfo = NULL + }; + + return true; +} + +static void setLayout(gpu_texture* texture, VkImageLayout layout, VkPipelineStageFlags nextStages, VkAccessFlags nextActions) { + if (texture->layout == layout) { + return; + } + + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = nextActions, + .oldLayout = texture->layout, + .newLayout = layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture->handle, + .subresourceRange = { texture->aspect, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS } + }; + + // TODO Wait for nothing, but could we opportunistically sync with other pending writes? Or is that weird + VkPipelineStageFlags waitFor = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + vkCmdPipelineBarrier(state.frame->commandBuffer, waitFor, nextStages, 0, 0, NULL, 0, NULL, 1, &barrier); + texture->layout = layout; +} + +static void nickname(void* handle, VkObjectType type, const char* name) { + if (name && state.config.debug) { + VkDebugUtilsObjectNameInfoEXT info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .objectType = type, + .objectHandle = VOIDP_TO_U64(handle), + .pObjectName = name + }; + + GPU_VK(vkSetDebugUtilsObjectNameEXT(state.device, &info)); + } +} + +static VkBool32 debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, const VkDebugUtilsMessengerCallbackDataEXT* data, void* context) { + if (state.config.callback) { + bool severe = severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + state.config.callback(state.config.context, data->pMessage, severe); + } + + return VK_FALSE; +} + +static const char* getErrorString(VkResult result) { + switch (result) { + case VK_ERROR_OUT_OF_HOST_MEMORY: return "Out of CPU memory"; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "Out of GPU memory"; + case VK_ERROR_MEMORY_MAP_FAILED: return "Could not map memory"; + case VK_ERROR_DEVICE_LOST: return "Lost connection to GPU"; + case VK_ERROR_TOO_MANY_OBJECTS: return "Too many objects"; + case VK_ERROR_FORMAT_NOT_SUPPORTED: return "Unsupported format"; + default: return NULL; + } +} From f428ceacaf7fb5f8270d67b371f45c54a1f0fae8 Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 20 Feb 2020 15:52:41 -0800 Subject: [PATCH 11/44] core/gpu: gl backend compiles; --- src/core/gpu_gl.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 0f6810d9b..27f7354d9 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -11,6 +11,7 @@ X(glVertexBindingDivisor, GLVERTEXBINDINGDIVISOR)\ X(glVertexAttribBinding, GLVERTEXATTRIBBINDING)\ X(glVertexAttribFormat, GLVERTEXATTRIBFORMAT)\ + X(glVertexAttribIFormat, GLVERTEXATTRIBIFORMAT)\ X(glBindVertexBuffer, GLBINDVERTEXBUFFER)\ X(glCullFace, GLCULLFACE)\ X(glFrontFace, GLFRONTFACE)\ @@ -22,6 +23,8 @@ X(glBlendEquationSeparate, GLBLENDEQUATIONSEPARATE)\ X(glDrawArraysInstanced, GLDRAWARRAYSINSTANCED)\ X(glDrawElementsInstancedBaseVertex, GLDRAWELEMENTSINSTANCEDBASEVERTEX)\ + X(glMultiDrawArraysIndirect, GLMULTIDRAWARRAYSINDIRECT)\ + X(glMultiDrawElementsIndirect, GLMULTIDRAWELEMENTSINDIRECT)\ X(glDispatchCompute, GLDISPATCHCOMPUTE)\ X(glGenVertexArrays, GLGENVERTEXARRAYS)\ X(glDeleteVertexArrays, GLDELETEVERTEXARRAYS)\ @@ -32,6 +35,7 @@ X(glBufferStorage, GLBUFFERSTORAGE)\ X(glMapBufferRange, GLMAPBUFFERRANGE)\ X(glFlushMappedBufferRange, GLFLUSHMAPPEDBUFFERRANGE)\ + X(glUnmapBuffer, GLUNMAPBUFFER)\ X(glInvalidateBufferData, GLINVALIDATEBUFFERDATA)\ X(glGenTextures, GLGENTEXTURES)\ X(glDeleteTextures, GLDELETETEXTURES)\ @@ -43,14 +47,14 @@ X(glGenFramebuffers, GLGENFRAMEBUFFERS)\ X(glDeleteFramebuffers, GLDELETEFRAMEBUFFERS)\ X(glBindFramebuffer, GLBINDFRAMEBUFFER)\ - X(glFramebufferTexture2D, GLBINDFRAMEBUFFER)\ - X(glFramebufferTextureLayer, GLBINDFRAMEBUFFER)\ - X(glCheckFramebufferStatus, GLBINDFRAMEBUFFER)\ - X(glDrawBuffers, GLBINDFRAMEBUFFER)\ + X(glFramebufferTexture2D, GLFRAMEBUFFERTEXTURE2D)\ + X(glFramebufferTextureLayer, GLFRAMEBUFFERTEXTURELAYER)\ + X(glCheckFramebufferStatus, GLCHECKFRAMEBUFFERSTATUS)\ + X(glDrawBuffers, GLDRAWBUFFERS)\ X(glUseProgram, GLUSEPROGRAM)\ #define GL_DECLARE(fn, upper) static PFN##upper##PROC fn; -#define GL_LOAD(fn, upper) fn = (upper) config->getProcAddress(#fn); +#define GL_LOAD(fn, upper) fn = (PFN##upper##PROC) config->getProcAddress(#fn); // Types @@ -118,7 +122,7 @@ void gpu_frame_finish(void) { } void gpu_render_begin(gpu_canvas* canvas) { - glBindFramebuffer(canvas->id); + glBindFramebuffer(GL_FRAMEBUFFER, canvas->id); state.canvas = canvas; } @@ -246,7 +250,7 @@ void gpu_set_pipeline(gpu_pipeline* pipeline) { if (my->colorMask != new->colorMask) { glColorMask(new->colorMask & 0x8, new->colorMask & 0x4, new->colorMask & 0x2, new->colorMask & 0x1); - my->colorMask != new->colorMask; + my->colorMask = new->colorMask; } if (my->blend.enabled != new->blend.enabled) { @@ -305,26 +309,26 @@ static const GLenum drawModes[] = { }; void gpu_draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex) { - GLenum mode = drawModes[state.pipeline->drawMode]; + GLenum mode = drawModes[state.pipeline->info.drawMode]; glDrawArraysInstanced(mode, firstVertex, vertexCount, instanceCount); } void gpu_draw_indexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex) { - GLenum mode = drawModes[state.pipeline->drawMode]; - GLenum type = state.pipeline->indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; - GLvoid* offset = (GLvoid*) (firstIndex * (type == GL_UNSIGNED_SHORT ? 2 : 4)); + GLenum mode = drawModes[state.pipeline->info.drawMode]; + GLenum type = state.pipeline->info.indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + GLvoid* offset = (GLvoid*) ((uint64_t) firstIndex * (type == GL_UNSIGNED_SHORT ? 2 : 4)); glDrawElementsInstancedBaseVertex(mode, indexCount, type, offset, instanceCount, baseVertex); } void gpu_draw_indirect(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { - GLenum mode = drawModes[state.pipeline->drawMode]; + GLenum mode = drawModes[state.pipeline->info.drawMode]; glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->id); glMultiDrawArraysIndirect(mode, (GLvoid*) offset, drawCount, 0); } void gpu_draw_indirect_indexed(gpu_buffer* buffer, uint64_t offset, uint32_t drawCount) { - GLenum mode = drawModes[state.pipeline->drawMode]; - GLenum type = state.pipeline->indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + GLenum mode = drawModes[state.pipeline->info.drawMode]; + GLenum type = state.pipeline->info.indexStride == GPU_INDEX_U16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->id); glMultiDrawElementsIndirect(mode, type, (GLvoid*) offset, drawCount, 0); } @@ -378,7 +382,7 @@ void gpu_buffer_destroy(gpu_buffer* buffer) { glDeleteBuffers(1, &buffer->id); } -uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset) { +uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset, uint64_t size) { return buffer->data + offset; } @@ -451,8 +455,8 @@ void gpu_texture_write(gpu_texture* texture, uint8_t* data, uint16_t offset[4], switch (texture->target) { case GL_TEXTURE_2D: glTexSubImage2D(GL_TEXTURE_2D, mip, x, y, w, h, format, type, data); case GL_TEXTURE_3D: glTexSubImage3D(GL_TEXTURE_3D, mip, x, y, z, w, h, d, format, type, data); - case GL_TEXTURE_CUBE: glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + z, mip, x, y, w, h, format, type, data); - case GL_TEXTURE_ARRAY: glTexSubImage3D(GL_TEXTURE_2D_ARRAY, mip, x, y, i, w, h, n, format, type, data); + case GL_TEXTURE_CUBE_MAP: glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + z, mip, x, y, w, h, format, type, data); + case GL_TEXTURE_2D_ARRAY: glTexSubImage3D(GL_TEXTURE_2D_ARRAY, mip, x, y, i, w, h, n, format, type, data); default: break; } } @@ -466,7 +470,7 @@ size_t gpu_sizeof_canvas(void) { bool gpu_canvas_init(gpu_canvas* canvas, gpu_canvas_info* info) { glGenFramebuffers(1, &canvas->id); glBindFramebuffer(GL_FRAMEBUFFER, canvas->id); - uint32_t bufferCount = 0; + GLsizei bufferCount = 0; GLenum buffers[4] = { GL_NONE }; for (uint32_t i = 0; i < 4 && info->color[i].texture; i++, bufferCount++) { buffers[i] = GL_COLOR_ATTACHMENT0 + i; @@ -498,6 +502,7 @@ void gpu_canvas_destroy(gpu_canvas* canvas) { bool gpu_pipeline_init(gpu_pipeline* pipeline, gpu_pipeline_info* info) { pipeline->info = *info; + return true; } void gpu_pipeline_destroy(gpu_pipeline* pipeline) { From b9b8062c188b572ffc6fb95a8d9189db657dbff5 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 21 Feb 2020 01:48:58 -0800 Subject: [PATCH 12/44] core/gpu: texture views; --- src/core/gpu.h | 11 +++++++++ src/core/gpu_gl.c | 62 ++++++++++++++++++++++++++++------------------- src/core/gpu_vk.c | 20 ++++++--------- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index 22601cee4..b9c8631e2 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -68,6 +68,16 @@ typedef enum { GPU_TEXTURE_FORMAT_D24S8 } gpu_texture_format; +typedef struct { + gpu_texture* source; + gpu_texture_type type; + gpu_texture_format format; + uint32_t baseMipmap; + uint32_t mipmapCount; + uint32_t baseLayer; + uint32_t layerCount; +} gpu_texture_view_info; + typedef struct { gpu_texture_type type; gpu_texture_format format; @@ -80,6 +90,7 @@ typedef struct { size_t gpu_sizeof_texture(void); bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info); +bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info); void gpu_texture_destroy(gpu_texture* texture); void gpu_texture_write(gpu_texture* texture, uint8_t* data, uint16_t offset[4], uint16_t extent[4], uint16_t mip); diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 27f7354d9..be2fd75ed 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -401,36 +401,38 @@ void gpu_buffer_discard(gpu_buffer* buffer) { // Texture +static const GLenum textureTargets[] = { + [GPU_TEXTURE_TYPE_2D] = GL_TEXTURE_2D, + [GPU_TEXTURE_TYPE_3D] = GL_TEXTURE_3D, + [GPU_TEXTURE_TYPE_CUBE] = GL_TEXTURE_CUBE_MAP, + [GPU_TEXTURE_TYPE_ARRAY] = GL_TEXTURE_2D_ARRAY +}; + +static const struct { GLenum format, pixelFormat, pixelType; } textureFormats[] = { + [GPU_TEXTURE_FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, + [GPU_TEXTURE_FORMAT_RGBA4] = { GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE }, + [GPU_TEXTURE_FORMAT_R16F] = { GL_R16F, GL_RED, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_RG16F] = { GL_RG16F, GL_RG, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_RGBA16F] = { GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, + [GPU_TEXTURE_FORMAT_R32F] = { GL_R32F, GL_RED, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RG32F] = { GL_RG32F, GL_RG, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RGBA32F] = { GL_RGBA32F, GL_RGBA, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_RGB10A2] = { GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, + [GPU_TEXTURE_FORMAT_RG11B10F] = { GL_R11F_G11F_B10F, GL_RGBA, GL_UNSIGNED_INT_10F_11F_11F_REV }, + [GPU_TEXTURE_FORMAT_D16] = { GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, + [GPU_TEXTURE_FORMAT_D32F] = { GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT }, + [GPU_TEXTURE_FORMAT_D24S8] = { GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 } +}; + size_t gpu_sizeof_texture(void) { return sizeof(gpu_texture); } bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { - static const GLenum targets[] = { - [GPU_TEXTURE_TYPE_2D] = GL_TEXTURE_2D, - [GPU_TEXTURE_TYPE_3D] = GL_TEXTURE_3D, - [GPU_TEXTURE_TYPE_CUBE] = GL_TEXTURE_CUBE_MAP, - [GPU_TEXTURE_TYPE_ARRAY] = GL_TEXTURE_2D_ARRAY - }; - static const struct { GLenum format, pixelFormat, pixelType; } formats[] = { - [GPU_TEXTURE_FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, - [GPU_TEXTURE_FORMAT_RGBA4] = { GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE }, - [GPU_TEXTURE_FORMAT_R16F] = { GL_R16F, GL_RED, GL_HALF_FLOAT }, - [GPU_TEXTURE_FORMAT_RG16F] = { GL_RG16F, GL_RG, GL_HALF_FLOAT }, - [GPU_TEXTURE_FORMAT_RGBA16F] = { GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, - [GPU_TEXTURE_FORMAT_R32F] = { GL_R32F, GL_RED, GL_FLOAT }, - [GPU_TEXTURE_FORMAT_RG32F] = { GL_RG32F, GL_RG, GL_FLOAT }, - [GPU_TEXTURE_FORMAT_RGBA32F] = { GL_RGBA32F, GL_RGBA, GL_FLOAT }, - [GPU_TEXTURE_FORMAT_RGB10A2] = { GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, - [GPU_TEXTURE_FORMAT_RG11B10F] = { GL_R11F_G11F_B10F, GL_RGBA, GL_UNSIGNED_INT_10F_11F_11F_REV }, - [GPU_TEXTURE_FORMAT_D16] = { GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, - [GPU_TEXTURE_FORMAT_D32F] = { GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT }, - [GPU_TEXTURE_FORMAT_D24S8] = { GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 } - }; - texture->target = targets[info->type]; - texture->format = formats[info->format].format; - texture->pixelFormat = formats[info->format].pixelFormat; - texture->pixelType = formats[info->format].pixelType; + texture->target = textureTargets[info->type]; + texture->format = textureTormats[info->format].format; + texture->pixelFormat = textureTormats[info->format].pixelFormat; + texture->pixelType = textureTormats[info->format].pixelType; glGenTextures(1, &texture->id); glBindTexture(texture->target, texture->id); if (info->type == GPU_TEXTURE_TYPE_2D || info->type == GPU_TEXTURE_TYPE_CUBE) { @@ -442,6 +444,16 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { return true; } +bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) { + texture->target = textureTargets[info->type]; + texture->format = textureTormats[info->format].format; + texture->pixelFormat = textureTormats[info->format].pixelFormat; + texture->pixelType = textureTormats[info->format].pixelType; + glGenTextures(1, &texture->id); + glTextureView(texture->id, texture->target, info->source, texture->format, info->baseMipmap, info->mipmapCount, info->baseLayer, info->layerCount); + return true; +} + void gpu_texture_destroy(gpu_texture* texture) { glDeleteTextures(1, &texture->id); } diff --git a/src/core/gpu_vk.c b/src/core/gpu_vk.c index 50726a73d..9c9de91a6 100644 --- a/src/core/gpu_vk.c +++ b/src/core/gpu_vk.c @@ -767,27 +767,24 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) { texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; - /* - if (!gpu_texture_init_view(texture, texture, NULL)) { + if (!gpu_texture_init_view(texture, &(gpu_texture_view_info) { .source = texture })) { vkDestroyImage(state.device, texture->handle, NULL); vkFreeMemory(state.device, texture->memory, NULL); return false; } - */ return true; } -/* -bool gpu_texture_init_view(gpu_texture* texture, gpu_texture* source, gpu_texture_view_info* info) { - if (texture != source) { +bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) { + if (texture != info->source) { texture->handle = VK_NULL_HANDLE; texture->memory = VK_NULL_HANDLE; texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; - texture->source = source; - texture->type = info ? info->type : source->type; - texture->format = (info && info->format) ? convertTextureFormat(info->format) : source->format; - texture->aspect = source->aspect; + texture->source = info->source; + texture->type = info ? info->type : info->source->type; + texture->format = textureInfo[info->format].format; + texture->aspect = info->source->aspect; } VkImageViewType type; @@ -801,7 +798,7 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture* source, gpu_textur VkImageViewCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = source->handle, + .image = info->source->handle, .viewType = type, .format = texture->format, .subresourceRange = { @@ -817,7 +814,6 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture* source, gpu_textur return true; } -*/ void gpu_texture_destroy(gpu_texture* texture) { if (texture->handle) condemn(texture->handle, VK_OBJECT_TYPE_IMAGE); From 93d556b60398085594abd02a38f804b851cc2b17 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 21 Feb 2020 15:44:48 -0800 Subject: [PATCH 13/44] zip: read compressed size from central directory; Some zip writers do not write the compressed size to the local file headers. --- src/core/zip.c | 6 +++--- src/core/zip.h | 3 ++- src/modules/filesystem/filesystem.c | 6 ++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/zip.c b/src/core/zip.c index 93e03aaa0..7c1ebb737 100644 --- a/src/core/zip.c +++ b/src/core/zip.c @@ -54,6 +54,7 @@ bool zip_next(zip_state* zip, zip_file* file) { file->mtime = readu16(p + 12); file->mdate = readu16(p + 14); + file->csize = readu32(p + 20); file->size = readu32(p + 24); file->length = readu16(p + 28); file->offset = readu32(p + 42) + zip->base; @@ -62,7 +63,7 @@ bool zip_next(zip_state* zip, zip_file* file) { return zip->cursor < zip->size; } -void* zip_load(zip_state* zip, size_t offset, size_t* csize, bool* compressed) { +void* zip_load(zip_state* zip, size_t offset, bool* compressed) { if (zip->size < 30 || offset > zip->size - 30) { return NULL; } @@ -78,7 +79,6 @@ void* zip_load(zip_state* zip, size_t offset, size_t* csize, bool* compressed) { return false; } - *csize = readu32(p + 18); uint32_t skip = readu16(p + 26) + readu16(p + 28); - return offset + 30 + skip + *csize > zip->size ? NULL : (p + 30 + skip); + return p + 30 + skip; } diff --git a/src/core/zip.h b/src/core/zip.h index 8c57d6779..97ef2a809 100644 --- a/src/core/zip.h +++ b/src/core/zip.h @@ -23,6 +23,7 @@ typedef struct { typedef struct { uint64_t offset; + uint64_t csize; uint64_t size; const char* name; uint16_t length; @@ -32,4 +33,4 @@ typedef struct { bool zip_open(zip_state* zip); bool zip_next(zip_state* zip, zip_file* info); -void* zip_load(zip_state* zip, size_t offset, size_t* csize, bool* compressed); +void* zip_load(zip_state* zip, size_t offset, bool* compressed); diff --git a/src/modules/filesystem/filesystem.c b/src/modules/filesystem/filesystem.c index bb166008a..59a765534 100755 --- a/src/modules/filesystem/filesystem.c +++ b/src/modules/filesystem/filesystem.c @@ -31,6 +31,7 @@ typedef struct { uint32_t nextSibling; size_t filename; uint64_t offset; + uint64_t csize; uint16_t mdate; uint16_t mtime; FileInfo info; @@ -547,11 +548,11 @@ static bool zip_read(Archive* archive, const char* path, size_t bytes, size_t* b } size_t dstSize = node->info.size; - size_t srcSize; + size_t srcSize = node->csize; bool compressed; const void* src; - if ((src = zip_load(&archive->zip, node->offset, &srcSize, &compressed)) == NULL) { + if ((src = zip_load(&archive->zip, node->offset, &compressed)) == NULL) { *dst = NULL; return true; } @@ -629,6 +630,7 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo .nextSibling = ~0u, .filename = (size_t) -1, .offset = info.offset, + .csize = info.csize, .mdate = info.mdate, .mtime = info.mtime, .info.size = info.size, From 3d3fb71a98e361509509974cde60dfd18308ba4b Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 21 Feb 2020 18:08:10 -0800 Subject: [PATCH 14/44] core/gpu: start shader stuff; coherent buffer mapping; --- src/core/gpu.h | 12 ++++-------- src/core/gpu_gl.c | 27 +++++++++++++-------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index b9c8631e2..c25b7e25b 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -32,7 +32,6 @@ size_t gpu_sizeof_buffer(void); bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info); void gpu_buffer_destroy(gpu_buffer* buffer); uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset, uint64_t size); -void gpu_buffer_flush(gpu_buffer* buffer, uint64_t offset, uint64_t size); void gpu_buffer_discard(gpu_buffer* buffer); // Texture @@ -135,19 +134,16 @@ void gpu_canvas_destroy(gpu_canvas* canvas); // Shader -typedef enum { - GPU_SHADER_VERTEX, - GPU_SHADER_FRAGMENT, - GPU_SHADER_COMPUTE -} gpu_shader_stage; - typedef struct { const void* code; size_t size; + const char* entry; } gpu_shader_source; typedef struct { - gpu_shader_source vertex, fragment, compute; + gpu_shader_source vertex; + gpu_shader_source fragment; + gpu_shader_source compute; const char* label; } gpu_shader_info; diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index be2fd75ed..946c58006 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -51,6 +51,8 @@ X(glFramebufferTextureLayer, GLFRAMEBUFFERTEXTURELAYER)\ X(glCheckFramebufferStatus, GLCHECKFRAMEBUFFERSTATUS)\ X(glDrawBuffers, GLDRAWBUFFERS)\ + X(glCreateProgram, GLCREATEPROGRAM)\ + X(glDeleteProgram, GLDELETEPROGRAM)\ X(glUseProgram, GLUSEPROGRAM)\ #define GL_DECLARE(fn, upper) static PFN##upper##PROC fn; @@ -370,10 +372,9 @@ bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) { else buffer->target = GL_TRANSFORM_FEEDBACK_BUFFER; // Haha no one uses this r-right glGenBuffers(1, &buffer->id); glBindBuffer(buffer->target, buffer->id); - GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; + GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; glBufferStorage(buffer->target, info->size, info->data, flags); - GLbitfield access = flags | GL_MAP_FLUSH_EXPLICIT_BIT; - buffer->data = glMapBufferRange(buffer->target, 0, info->size, access); + buffer->data = glMapBufferRange(buffer->target, 0, info->size, flags); buffer->size = info->size; return true; } @@ -386,17 +387,8 @@ uint8_t* gpu_buffer_map(gpu_buffer* buffer, uint64_t offset, uint64_t size) { return buffer->data + offset; } -void gpu_buffer_flush(gpu_buffer* buffer, uint64_t offset, uint64_t size) { - glBindBuffer(buffer->target, buffer->id); - glFlushMappedBufferRange(buffer->target, offset, size); -} - void gpu_buffer_discard(gpu_buffer* buffer) { - glBindBuffer(buffer->target, buffer->id); - glUnmapBuffer(buffer->target); - glInvalidateBufferData(buffer->target); - GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - buffer->data = glMapBufferRange(buffer->target, 0, buffer->size, flags); + glInvalidateBufferData(buffer->id); } // Texture @@ -508,7 +500,14 @@ void gpu_canvas_destroy(gpu_canvas* canvas) { // Shader -/* */ +bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) { + shader->id = glCreateProgram(); + return true; +} + +void gpu_shader_destroy(gpu_shader* shader) { + glDeleteProgram(shader->id); +} // Pipeline From 2cf6d7b10952b3d8f3070ae484d2e1d59d3118a8 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 00:30:30 -0800 Subject: [PATCH 15/44] Improve lovr.filesystem.load errors; Lua was happily compiling nil chunks and making them return empty strings, which was not a good error experience in situations where your file couldn't be loaded properly. Now we return nil plus an error message, which matches LOVE and other Lua conventions. --- src/api/l_filesystem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/api/l_filesystem.c b/src/api/l_filesystem.c index 1d787edfb..24f4ee7e0 100644 --- a/src/api/l_filesystem.c +++ b/src/api/l_filesystem.c @@ -59,6 +59,11 @@ static void pushDirectoryItem(void* context, const char* path) { static int luax_loadfile(lua_State* L, const char* path, const char* debug) { size_t size; void* buffer = luax_readfile(path, &size); + if (!buffer) { + lua_pushnil(L); + lua_pushfstring(L, "Could not load file '%s'", path); + return 2; + } int status = luaL_loadbuffer(L, buffer, size, debug); free(buffer); switch (status) { From 5d8bf1d45aab040f365f28357b56f06899f37838 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 01:01:17 -0800 Subject: [PATCH 16/44] Fix Source:setLooping assert; --- src/modules/audio/source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/audio/source.c b/src/modules/audio/source.c index 81b51cc59..39cca65f8 100644 --- a/src/modules/audio/source.c +++ b/src/modules/audio/source.c @@ -238,7 +238,7 @@ void lovrSourceSetFalloff(Source* source, float reference, float max, float roll } void lovrSourceSetLooping(Source* source, bool isLooping) { - lovrAssert(!source->stream || lovrAudioStreamIsRaw(source->stream), "Can't loop a raw stream"); + lovrAssert(!source->stream || !lovrAudioStreamIsRaw(source->stream), "Can't loop a raw stream"); source->isLooping = isLooping; if (source->type == SOURCE_STATIC) { alSourcei(source->id, AL_LOOPING, isLooping ? AL_TRUE : AL_FALSE); From b045d4278204ed75b6b53fd4669dc51ce92b3574 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 21:43:36 -0800 Subject: [PATCH 17/44] Opaque Buffer; --- src/modules/graphics/buffer.c | 13 ------------- src/modules/graphics/buffer.h | 19 +++---------------- src/modules/graphics/opengl.c | 28 +++++++++++++++++++++++++++- src/modules/graphics/opengl.h | 4 ---- 4 files changed, 30 insertions(+), 34 deletions(-) delete mode 100644 src/modules/graphics/buffer.c diff --git a/src/modules/graphics/buffer.c b/src/modules/graphics/buffer.c deleted file mode 100644 index 409030dce..000000000 --- a/src/modules/graphics/buffer.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "graphics/buffer.h" - -size_t lovrBufferGetSize(Buffer* buffer) { - return buffer->size; -} - -bool lovrBufferIsReadable(Buffer* buffer) { - return buffer->readable; -} - -BufferUsage lovrBufferGetUsage(Buffer* buffer) { - return buffer->usage; -} diff --git a/src/modules/graphics/buffer.h b/src/modules/graphics/buffer.h index a637e24c0..d7cf4281a 100644 --- a/src/modules/graphics/buffer.h +++ b/src/modules/graphics/buffer.h @@ -1,6 +1,5 @@ -#include "graphics/opengl.h" -#include #include +#include #pragma once @@ -19,20 +18,8 @@ typedef enum { USAGE_STREAM } BufferUsage; -typedef struct Buffer { - void* data; - size_t size; - size_t flushFrom; - size_t flushTo; - BufferType type; - BufferUsage usage; - bool mapped; - bool readable; - GPU_BUFFER_FIELDS -} Buffer; - -Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferType type, BufferUsage usage, bool readable); -#define lovrBufferCreate(...) lovrBufferInit(lovrAlloc(Buffer), __VA_ARGS__) +typedef struct Buffer Buffer; +Buffer* lovrBufferCreate(size_t size, void* data, BufferType type, BufferUsage usage, bool readable); void lovrBufferDestroy(void* ref); size_t lovrBufferGetSize(Buffer* buffer); bool lovrBufferIsReadable(Buffer* buffer); diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 616a3af75..7f12e536f 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -31,6 +31,19 @@ #define LOVR_SHADER_BONE_WEIGHTS 6 #define LOVR_SHADER_DRAW_ID 7 +struct Buffer { + uint32_t id; + void* data; + size_t size; + size_t flushFrom; + size_t flushTo; + BufferType type; + BufferUsage usage; + bool mapped; + bool readable; + uint8_t incoherent; +}; + typedef enum { BARRIER_BLOCK, BARRIER_UNIFORM_TEXTURE, @@ -1830,7 +1843,8 @@ TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index) { // Buffer -Buffer* lovrBufferInit(Buffer* buffer, size_t size, void* data, BufferType type, BufferUsage usage, bool readable) { +Buffer* lovrBufferCreate(size_t size, void* data, BufferType type, BufferUsage usage, bool readable) { + Buffer* buffer = lovrAlloc(Buffer); state.stats.bufferCount++; state.stats.bufferMemory += size; buffer->size = size; @@ -1873,6 +1887,18 @@ void lovrBufferDestroy(void* ref) { state.stats.bufferCount--; } +size_t lovrBufferGetSize(Buffer* buffer) { + return buffer->size; +} + +bool lovrBufferIsReadable(Buffer* buffer) { + return buffer->readable; +} + +BufferUsage lovrBufferGetUsage(Buffer* buffer) { + return buffer->usage; +} + void* lovrBufferMap(Buffer* buffer, size_t offset) { #ifndef LOVR_WEBGL if (!GLAD_GL_ARB_buffer_storage && !buffer->mapped) { diff --git a/src/modules/graphics/opengl.h b/src/modules/graphics/opengl.h index 4e20f3fcf..de7e5f608 100644 --- a/src/modules/graphics/opengl.h +++ b/src/modules/graphics/opengl.h @@ -11,10 +11,6 @@ #pragma once -#define GPU_BUFFER_FIELDS \ - uint8_t incoherent; \ - uint32_t id; - #define GPU_CANVAS_FIELDS \ bool immortal; \ uint32_t framebuffer; \ From 52d4f7e5202ddd89c9ff4183fe0f7d445723bac5 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 22:03:20 -0800 Subject: [PATCH 18/44] Opaque Texture; --- src/modules/graphics/canvas.c | 2 +- src/modules/graphics/opengl.c | 67 +++++++++++++++++++++++++++++++++- src/modules/graphics/opengl.h | 7 ---- src/modules/graphics/texture.c | 37 ------------------- src/modules/graphics/texture.h | 25 ++----------- src/modules/headset/openxr.c | 8 ++-- 6 files changed, 73 insertions(+), 73 deletions(-) delete mode 100644 src/modules/graphics/texture.c diff --git a/src/modules/graphics/canvas.c b/src/modules/graphics/canvas.c index 2563a86bb..96f816a6f 100644 --- a/src/modules/graphics/canvas.c +++ b/src/modules/graphics/canvas.c @@ -32,7 +32,7 @@ void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t lovrAssert(!hasDepthBuffer || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width); lovrAssert(!hasDepthBuffer || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height); #ifndef __ANDROID__ // On multiview canvases, the multisample settings can be different - lovrAssert(texture->msaa == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA"); + lovrAssert(lovrTextureGetMSAA(texture) == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA"); #endif lovrRetain(texture); } diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 7f12e536f..5888d26ba 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -44,6 +44,27 @@ struct Buffer { uint8_t incoherent; }; +struct Texture { + GLuint id; + GLuint msaaId; + GLenum target; + TextureType type; + TextureFormat format; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t mipmapCount; + CompareMode compareMode; + TextureFilter filter; + TextureWrap wrap; + uint32_t msaa; + bool srgb; + bool mipmaps; + bool allocated; + bool native; + uint8_t incoherent; +}; + typedef enum { BARRIER_BLOCK, BARRIER_UNIFORM_TEXTURE, @@ -1442,7 +1463,8 @@ const GpuStats* lovrGpuGetStats() { // Texture -Texture* lovrTextureInit(Texture* texture, TextureType type, TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa) { +Texture* lovrTextureCreate(TextureType type, TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa) { + Texture* texture = lovrAlloc(Texture); state.stats.textureCount++; texture->type = type; texture->srgb = srgb; @@ -1470,7 +1492,8 @@ Texture* lovrTextureInit(Texture* texture, TextureType type, TextureData** slice return texture; } -Texture* lovrTextureInitFromHandle(Texture* texture, uint32_t handle, TextureType type, uint32_t depth) { +Texture* lovrTextureCreateFromHandle(uint32_t handle, TextureType type, uint32_t depth) { + Texture* texture = lovrAlloc(Texture); state.stats.textureCount++; texture->type = type; texture->id = handle; @@ -1637,6 +1660,46 @@ void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, uint32 } } +uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap) { + return MAX(texture->width >> mipmap, 1); +} + +uint32_t lovrTextureGetHeight(Texture* texture, uint32_t mipmap) { + return MAX(texture->height >> mipmap, 1); +} + +uint32_t lovrTextureGetDepth(Texture* texture, uint32_t mipmap) { + return texture->type == TEXTURE_VOLUME ? MAX(texture->depth >> mipmap, 1) : texture->depth; +} + +uint32_t lovrTextureGetMipmapCount(Texture* texture) { + return texture->mipmapCount; +} + +uint32_t lovrTextureGetMSAA(Texture* texture) { + return texture->msaa; +} + +TextureType lovrTextureGetType(Texture* texture) { + return texture->type; +} + +TextureFormat lovrTextureGetFormat(Texture* texture) { + return texture->format; +} + +CompareMode lovrTextureGetCompareMode(Texture* texture) { + return texture->compareMode; +} + +TextureFilter lovrTextureGetFilter(Texture* texture) { + return texture->filter; +} + +TextureWrap lovrTextureGetWrap(Texture* texture) { + return texture->wrap; +} + void lovrTextureSetCompareMode(Texture* texture, CompareMode compareMode) { if (texture->compareMode != compareMode) { lovrGraphicsFlush(); diff --git a/src/modules/graphics/opengl.h b/src/modules/graphics/opengl.h index de7e5f608..24f0f0f27 100644 --- a/src/modules/graphics/opengl.h +++ b/src/modules/graphics/opengl.h @@ -23,10 +23,3 @@ #define GPU_SHADER_FIELDS \ uint32_t program; - -#define GPU_TEXTURE_FIELDS \ - bool native; \ - uint8_t incoherent; \ - GLuint id; \ - GLuint msaaId; \ - GLenum target; diff --git a/src/modules/graphics/texture.c b/src/modules/graphics/texture.c deleted file mode 100644 index 9af9586e2..000000000 --- a/src/modules/graphics/texture.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "graphics/texture.h" - -uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap) { - return MAX(texture->width >> mipmap, 1); -} - -uint32_t lovrTextureGetHeight(Texture* texture, uint32_t mipmap) { - return MAX(texture->height >> mipmap, 1); -} - -uint32_t lovrTextureGetDepth(Texture* texture, uint32_t mipmap) { - return texture->type == TEXTURE_VOLUME ? MAX(texture->depth >> mipmap, 1) : texture->depth; -} - -uint32_t lovrTextureGetMipmapCount(Texture* texture) { - return texture->mipmapCount; -} - -TextureType lovrTextureGetType(Texture* texture) { - return texture->type; -} - -TextureFormat lovrTextureGetFormat(Texture* texture) { - return texture->format; -} - -CompareMode lovrTextureGetCompareMode(Texture* texture) { - return texture->compareMode; -} - -TextureFilter lovrTextureGetFilter(Texture* texture) { - return texture->filter; -} - -TextureWrap lovrTextureGetWrap(Texture* texture) { - return texture->wrap; -} diff --git a/src/modules/graphics/texture.h b/src/modules/graphics/texture.h index 21e1d8855..0dfbb5ebf 100644 --- a/src/modules/graphics/texture.h +++ b/src/modules/graphics/texture.h @@ -1,6 +1,5 @@ #include "data/textureData.h" #include "graphics/graphics.h" -#include "graphics/opengl.h" #include "data/modelData.h" #include @@ -15,27 +14,9 @@ typedef enum { TEXTURE_VOLUME } TextureType; -typedef struct Texture { - TextureType type; - TextureFormat format; - uint32_t width; - uint32_t height; - uint32_t depth; - uint32_t mipmapCount; - CompareMode compareMode; - TextureFilter filter; - TextureWrap wrap; - uint32_t msaa; - bool srgb; - bool mipmaps; - bool allocated; - GPU_TEXTURE_FIELDS -} Texture; - -Texture* lovrTextureInit(Texture* texture, TextureType type, struct TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa); -Texture* lovrTextureInitFromHandle(Texture* texture, uint32_t handle, TextureType type, uint32_t depth); -#define lovrTextureCreate(...) lovrTextureInit(lovrAlloc(Texture), __VA_ARGS__) -#define lovrTextureCreateFromHandle(...) lovrTextureInitFromHandle(lovrAlloc(Texture), __VA_ARGS__) +typedef struct Texture Texture; +Texture* lovrTextureCreate(TextureType type, struct TextureData** slices, uint32_t sliceCount, bool srgb, bool mipmaps, uint32_t msaa); +Texture* lovrTextureCreateFromHandle(uint32_t handle, TextureType type, uint32_t depth); void lovrTextureDestroy(void* ref); void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint32_t depth, TextureFormat format); void lovrTextureReplacePixels(Texture* texture, struct TextureData* data, uint32_t x, uint32_t y, uint32_t slice, uint32_t mipmap); diff --git a/src/modules/headset/openxr.c b/src/modules/headset/openxr.c index 209f3aeb0..e332846ff 100644 --- a/src/modules/headset/openxr.c +++ b/src/modules/headset/openxr.c @@ -203,7 +203,7 @@ static struct { XrCompositionLayerProjectionView layerViews[2]; XrFrameState frameState; Canvas* canvas; - Texture textures[MAX_IMAGES]; + Texture* textures[MAX_IMAGES]; uint32_t imageCount; uint32_t msaa; uint32_t width; @@ -394,7 +394,7 @@ static bool openxr_init(float offset, uint32_t msaa) { XR_INIT(xrEnumerateSwapchainImages(state.swapchain, MAX_IMAGES, &state.imageCount, (XrSwapchainImageBaseHeader*) images)); for (uint32_t i = 0; i < state.imageCount; i++) { - lovrTextureInitFromHandle(&state.textures[i], images[i].image, TEXTURE_2D, 1); + state.textures[i] = lovrTextureCreateFromHandle(images[i].image, TEXTURE_2D, 1); } // Pre-init composition layer @@ -425,7 +425,7 @@ static bool openxr_init(float offset, uint32_t msaa) { static void openxr_destroy() { lovrRelease(Canvas, state.canvas); for (uint32_t i = 0; i < state.imageCount; i++) { - lovrRelease(Texture, &state.textures[i]); + lovrRelease(Texture, state.textures[i]); } for (size_t i = 0; i < MAX_ACTIONS; i++) { @@ -721,7 +721,7 @@ static void openxr_renderTo(void (*callback)(void*), void* userdata) { mat4_invert(camera.viewMatrix[eye]); } - lovrCanvasSetAttachments(state.canvas, &(Attachment) { &state.textures[imageIndex], 0, 0 }, 1); + lovrCanvasSetAttachments(state.canvas, &(Attachment) { state.textures[imageIndex], 0, 0 }, 1); lovrGraphicsSetCamera(&camera, true); callback(userdata); lovrGraphicsSetCamera(NULL, false); From d034e8c01be5ff4ad4652897ca236bb4628477f4 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 22:19:59 -0800 Subject: [PATCH 19/44] Opaque Canvas; --- src/modules/graphics/canvas.c | 67 ----------------------- src/modules/graphics/canvas.h | 23 +++----- src/modules/graphics/graphics.c | 8 +-- src/modules/graphics/opengl.c | 96 ++++++++++++++++++++++++++++++++- src/modules/graphics/opengl.h | 6 --- 5 files changed, 104 insertions(+), 96 deletions(-) delete mode 100644 src/modules/graphics/canvas.c diff --git a/src/modules/graphics/canvas.c b/src/modules/graphics/canvas.c deleted file mode 100644 index 96f816a6f..000000000 --- a/src/modules/graphics/canvas.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "graphics/canvas.h" -#include "graphics/graphics.h" -#include "core/ref.h" -#include - -const Attachment* lovrCanvasGetAttachments(Canvas* canvas, uint32_t* count) { - if (count) *count = canvas->attachmentCount; - return canvas->attachments; -} - -void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t count) { - lovrAssert(count > 0, "A Canvas must have at least one attached Texture"); - lovrAssert(count <= MAX_CANVAS_ATTACHMENTS, "Only %d textures can be attached to a Canvas, got %d\n", MAX_CANVAS_ATTACHMENTS, count); - - if (!canvas->needsAttach && count == canvas->attachmentCount && !memcmp(canvas->attachments, attachments, count * sizeof(Attachment))) { - return; - } - - lovrGraphicsFlushCanvas(canvas); - - for (uint32_t i = 0; i < count; i++) { - Texture* texture = attachments[i].texture; - uint32_t slice = attachments[i].slice; - uint32_t level = attachments[i].level; - uint32_t width = lovrTextureGetWidth(texture, level); - uint32_t height = lovrTextureGetHeight(texture, level); - uint32_t depth = lovrTextureGetDepth(texture, level); - uint32_t mipmaps = lovrTextureGetMipmapCount(texture); - bool hasDepthBuffer = canvas->flags.depth.enabled; - lovrAssert(slice < depth, "Invalid attachment slice (Texture has %d, got %d)", depth, slice + 1); - lovrAssert(level < mipmaps, "Invalid attachment mipmap level (Texture has %d, got %d)", mipmaps, level + 1); - lovrAssert(!hasDepthBuffer || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width); - lovrAssert(!hasDepthBuffer || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height); -#ifndef __ANDROID__ // On multiview canvases, the multisample settings can be different - lovrAssert(lovrTextureGetMSAA(texture) == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA"); -#endif - lovrRetain(texture); - } - - for (uint32_t i = 0; i < canvas->attachmentCount; i++) { - lovrRelease(Texture, canvas->attachments[i].texture); - } - - memcpy(canvas->attachments, attachments, count * sizeof(Attachment)); - canvas->attachmentCount = count; - canvas->needsAttach = true; -} - -bool lovrCanvasIsStereo(Canvas* canvas) { - return canvas->flags.stereo; -} - -uint32_t lovrCanvasGetWidth(Canvas* canvas) { - return canvas->width; -} - -uint32_t lovrCanvasGetHeight(Canvas* canvas) { - return canvas->height; -} - -uint32_t lovrCanvasGetMSAA(Canvas* canvas) { - return canvas->flags.msaa; -} - -Texture* lovrCanvasGetDepthTexture(Canvas* canvas) { - return canvas->depth.texture; -} diff --git a/src/modules/graphics/canvas.h b/src/modules/graphics/canvas.h index 9aeb02eb6..350512b1c 100644 --- a/src/modules/graphics/canvas.h +++ b/src/modules/graphics/canvas.h @@ -1,5 +1,4 @@ #include "graphics/texture.h" -#include "graphics/opengl.h" #pragma once @@ -25,29 +24,19 @@ typedef struct { bool mipmaps; } CanvasFlags; -typedef struct Canvas { - uint32_t width; - uint32_t height; - CanvasFlags flags; - Attachment attachments[MAX_CANVAS_ATTACHMENTS]; - Attachment depth; - uint32_t attachmentCount; - bool needsAttach; - bool needsResolve; - GPU_CANVAS_FIELDS -} Canvas; - -Canvas* lovrCanvasInit(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags); -Canvas* lovrCanvasInitFromHandle(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal); -#define lovrCanvasCreate(...) lovrCanvasInit(lovrAlloc(Canvas), __VA_ARGS__) -#define lovrCanvasCreateFromHandle(...) lovrCanvasInitFromHandle(lovrAlloc(Canvas), __VA_ARGS__) +typedef struct Canvas Canvas; +Canvas* lovrCanvasCreate(uint32_t width, uint32_t height, CanvasFlags flags); +Canvas* lovrCanvasCreateFromHandle(uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal); void lovrCanvasDestroy(void* ref); const Attachment* lovrCanvasGetAttachments(Canvas* canvas, uint32_t* count); void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t count); void lovrCanvasResolve(Canvas* canvas); bool lovrCanvasIsStereo(Canvas* canvas); +void lovrCanvasSetStereo(Canvas* canvas, bool stereo); uint32_t lovrCanvasGetWidth(Canvas* canvas); uint32_t lovrCanvasGetHeight(Canvas* canvas); +void lovrCanvasSetWidth(Canvas* canvas, uint32_t width); +void lovrCanvasSetHeight(Canvas* canvas, uint32_t height); uint32_t lovrCanvasGetMSAA(Canvas* canvas); struct Texture* lovrCanvasGetDepthTexture(Canvas* canvas); struct TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index); diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index cd488ac72..e284eb846 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -168,8 +168,8 @@ static void onCloseWindow(void) { static void onResizeWindow(int width, int height) { state.width = width; state.height = height; - state.defaultCanvas->width = width; - state.defaultCanvas->height = height; + lovrCanvasSetWidth(state.defaultCanvas, width); + lovrCanvasSetHeight(state.defaultCanvas, height); } static void* lovrGraphicsMapBuffer(StreamType type, uint32_t count) { @@ -303,13 +303,13 @@ void lovrGraphicsSetCamera(Camera* camera, bool clear) { mat4_perspective(state.camera.projection[0], .01f, 100.f, 67.f * (float) M_PI / 180.f, (float) state.width / state.height); mat4_perspective(state.camera.projection[1], .01f, 100.f, 67.f * (float) M_PI / 180.f, (float) state.width / state.height); state.camera.canvas = state.defaultCanvas; - state.camera.canvas->flags.stereo = false; + lovrCanvasSetStereo(state.camera.canvas, false); } else { state.camera = *camera; if (!state.camera.canvas) { state.camera.canvas = state.defaultCanvas; - state.camera.canvas->flags.stereo = camera->stereo; + lovrCanvasSetStereo(state.camera.canvas, camera->stereo); } } diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 5888d26ba..b47d87f72 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -65,6 +65,21 @@ struct Texture { uint8_t incoherent; }; +struct Canvas { + uint32_t framebuffer; + uint32_t resolveBuffer; + uint32_t depthBuffer; + uint32_t width; + uint32_t height; + CanvasFlags flags; + Attachment attachments[MAX_CANVAS_ATTACHMENTS]; + Attachment depth; + uint32_t attachmentCount; + bool needsAttach; + bool needsResolve; + bool immortal; +}; + typedef enum { BARRIER_BLOCK, BARRIER_UNIFORM_TEXTURE, @@ -1765,7 +1780,8 @@ void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) { // Canvas -Canvas* lovrCanvasInit(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags) { +Canvas* lovrCanvasCreate(uint32_t width, uint32_t height, CanvasFlags flags) { + Canvas* canvas = lovrAlloc(Canvas); if (flags.stereo && state.singlepass != MULTIVIEW) { width *= 2; } @@ -1809,7 +1825,8 @@ Canvas* lovrCanvasInit(Canvas* canvas, uint32_t width, uint32_t height, CanvasFl return canvas; } -Canvas* lovrCanvasInitFromHandle(Canvas* canvas, uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal) { +Canvas* lovrCanvasCreateFromHandle(uint32_t width, uint32_t height, CanvasFlags flags, uint32_t framebuffer, uint32_t depthBuffer, uint32_t resolveBuffer, uint32_t attachmentCount, bool immortal) { + Canvas* canvas = lovrAlloc(Canvas); canvas->framebuffer = framebuffer; canvas->depthBuffer = depthBuffer; canvas->resolveBuffer = resolveBuffer; @@ -1904,6 +1921,81 @@ TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index) { return textureData; } +const Attachment* lovrCanvasGetAttachments(Canvas* canvas, uint32_t* count) { + if (count) *count = canvas->attachmentCount; + return canvas->attachments; +} + +void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, uint32_t count) { + lovrAssert(count > 0, "A Canvas must have at least one attached Texture"); + lovrAssert(count <= MAX_CANVAS_ATTACHMENTS, "Only %d textures can be attached to a Canvas, got %d\n", MAX_CANVAS_ATTACHMENTS, count); + + if (!canvas->needsAttach && count == canvas->attachmentCount && !memcmp(canvas->attachments, attachments, count * sizeof(Attachment))) { + return; + } + + lovrGraphicsFlushCanvas(canvas); + + for (uint32_t i = 0; i < count; i++) { + Texture* texture = attachments[i].texture; + uint32_t slice = attachments[i].slice; + uint32_t level = attachments[i].level; + uint32_t width = lovrTextureGetWidth(texture, level); + uint32_t height = lovrTextureGetHeight(texture, level); + uint32_t depth = lovrTextureGetDepth(texture, level); + uint32_t mipmaps = lovrTextureGetMipmapCount(texture); + bool hasDepthBuffer = canvas->flags.depth.enabled; + lovrAssert(slice < depth, "Invalid attachment slice (Texture has %d, got %d)", depth, slice + 1); + lovrAssert(level < mipmaps, "Invalid attachment mipmap level (Texture has %d, got %d)", mipmaps, level + 1); + lovrAssert(!hasDepthBuffer || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width); + lovrAssert(!hasDepthBuffer || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height); +#ifndef __ANDROID__ // On multiview canvases, the multisample settings can be different + lovrAssert(lovrTextureGetMSAA(texture) == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA"); +#endif + lovrRetain(texture); + } + + for (uint32_t i = 0; i < canvas->attachmentCount; i++) { + lovrRelease(Texture, canvas->attachments[i].texture); + } + + memcpy(canvas->attachments, attachments, count * sizeof(Attachment)); + canvas->attachmentCount = count; + canvas->needsAttach = true; +} + +bool lovrCanvasIsStereo(Canvas* canvas) { + return canvas->flags.stereo; +} + +void lovrCanvasSetStereo(Canvas* canvas, bool stereo) { + canvas->flags.stereo = stereo; +} + +uint32_t lovrCanvasGetWidth(Canvas* canvas) { + return canvas->width; +} + +uint32_t lovrCanvasGetHeight(Canvas* canvas) { + return canvas->height; +} + +void lovrCanvasSetWidth(Canvas* canvas, uint32_t width) { + canvas->width = width; +} + +void lovrCanvasSetHeight(Canvas* canvas, uint32_t height) { + canvas->height = height; +} + +uint32_t lovrCanvasGetMSAA(Canvas* canvas) { + return canvas->flags.msaa; +} + +Texture* lovrCanvasGetDepthTexture(Canvas* canvas) { + return canvas->depth.texture; +} + // Buffer Buffer* lovrBufferCreate(size_t size, void* data, BufferType type, BufferUsage usage, bool readable) { diff --git a/src/modules/graphics/opengl.h b/src/modules/graphics/opengl.h index 24f0f0f27..6afd22f40 100644 --- a/src/modules/graphics/opengl.h +++ b/src/modules/graphics/opengl.h @@ -11,12 +11,6 @@ #pragma once -#define GPU_CANVAS_FIELDS \ - bool immortal; \ - uint32_t framebuffer; \ - uint32_t resolveBuffer; \ - uint32_t depthBuffer; - #define GPU_MESH_FIELDS \ uint32_t vao; \ uint32_t ibo; From aabb8cb8308880828d42dfe3315e4df980a71e51 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 22:42:05 -0800 Subject: [PATCH 20/44] Opaque Shader; --- src/modules/graphics/opengl.c | 275 +++++++++++++++++++++++++++++++++- src/modules/graphics/opengl.h | 3 - src/modules/graphics/shader.c | 259 -------------------------------- src/modules/graphics/shader.h | 33 +--- 4 files changed, 279 insertions(+), 291 deletions(-) delete mode 100644 src/modules/graphics/shader.c diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index b47d87f72..45c8c8a6c 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -8,6 +8,7 @@ #include "graphics/texture.h" #include "resources/shaders.h" #include "data/modelData.h" +#include "math/math.h" #include "core/hash.h" #include "core/ref.h" #include @@ -80,6 +81,24 @@ struct Canvas { bool immortal; }; +struct ShaderBlock { + BlockType type; + arr_uniform_t uniforms; + map_t uniformMap; + struct Buffer* buffer; +}; + +struct Shader { + uint32_t program; + ShaderType type; + arr_uniform_t uniforms; + arr_block_t blocks[2]; + map_t attributes; + map_t uniformMap; + map_t blockMap; + bool multiview; +}; + typedef enum { BARRIER_BLOCK, BARRIER_UNIFORM_TEXTURE, @@ -492,6 +511,58 @@ static TextureType getUniformTextureType(GLenum type) { } } +static size_t getUniformTypeLength(const Uniform* uniform) { + size_t size = 0; + + if (uniform->count > 1) { + size += 2 + floor(log10(uniform->count)) + 1; // "[count]" + } + + switch (uniform->type) { + case UNIFORM_MATRIX: size += 4; break; + case UNIFORM_FLOAT: size += uniform->components == 1 ? 5 : 4; break; + case UNIFORM_INT: size += uniform->components == 1 ? 3 : 5; break; + default: break; + } + + return size; +} + +static const char* getUniformTypeName(const Uniform* uniform) { + switch (uniform->type) { + case UNIFORM_FLOAT: + switch (uniform->components) { + case 1: return "float"; + case 2: return "vec2"; + case 3: return "vec3"; + case 4: return "vec4"; + } + break; + + case UNIFORM_INT: + switch (uniform->components) { + case 1: return "int"; + case 2: return "ivec2"; + case 3: return "ivec3"; + case 4: return "ivec4"; + } + break; + + case UNIFORM_MATRIX: + switch (uniform->components) { + case 2: return "mat2"; + case 3: return "mat3"; + case 4: return "mat4"; + } + break; + + default: break; + } + + lovrThrow("Unreachable"); + return ""; +} + // Syncing resources is only relevant for compute shaders #ifndef LOVR_WEBGL static void lovrGpuSync(uint8_t flags) { @@ -2401,7 +2472,8 @@ static char* lovrShaderGetFlagCode(ShaderFlag* flags, uint32_t flagCount) { return code; } -Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, int vertexSourceLength, const char* fragmentSource, int fragmentSourceLength, ShaderFlag* flags, uint32_t flagCount, bool multiview) { +Shader* lovrShaderCreateGraphics(const char* vertexSource, int vertexSourceLength, const char* fragmentSource, int fragmentSourceLength, ShaderFlag* flags, uint32_t flagCount, bool multiview) { + Shader* shader = lovrAlloc(Shader); #if defined(LOVR_WEBGL) || defined(LOVR_GLES) const char* version = "#version 300 es\n"; #else @@ -2481,7 +2553,20 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, int ver return shader; } -Shader* lovrShaderInitCompute(Shader* shader, const char* source, int length, ShaderFlag* flags, uint32_t flagCount) { +Shader* lovrShaderCreateDefault(DefaultShader type, ShaderFlag* flags, uint32_t flagCount, bool multiview) { + switch (type) { + case SHADER_UNLIT: return lovrShaderCreateGraphics(NULL, -1, NULL, -1, flags, flagCount, multiview); + case SHADER_STANDARD: return lovrShaderCreateGraphics(lovrStandardVertexShader, -1, lovrStandardFragmentShader, -1, flags, flagCount, multiview); + case SHADER_CUBE: return lovrShaderCreateGraphics(lovrCubeVertexShader, -1, lovrCubeFragmentShader, -1, flags, flagCount, multiview); + case SHADER_PANO: return lovrShaderCreateGraphics(lovrCubeVertexShader, -1, lovrPanoFragmentShader, -1, flags, flagCount, multiview); + case SHADER_FONT: return lovrShaderCreateGraphics(NULL, -1, lovrFontFragmentShader, -1, flags, flagCount, multiview); + case SHADER_FILL: return lovrShaderCreateGraphics(lovrFillVertexShader, -1, NULL, -1, flags, flagCount, multiview); + default: lovrThrow("Unknown default shader type"); return NULL; + } +} + +Shader* lovrShaderCreateCompute(const char* source, int length, ShaderFlag* flags, uint32_t flagCount) { + Shader* shader = lovrAlloc(Shader); #ifdef LOVR_WEBGL lovrThrow("Compute shaders are not supported on this system"); #else @@ -2524,6 +2609,192 @@ void lovrShaderDestroy(void* ref) { map_free(&shader->blockMap); } +ShaderType lovrShaderGetType(Shader* shader) { + return shader->type; +} + +int lovrShaderGetAttributeLocation(Shader* shader, const char* name) { + uint64_t location = map_get(&shader->attributes, hash64(name, strlen(name))); + return location == MAP_NIL ? -1 : (int) location; +} + +bool lovrShaderHasUniform(Shader* shader, const char* name) { + return map_get(&shader->uniformMap, hash64(name, strlen(name))) != MAP_NIL; +} + +bool lovrShaderHasBlock(Shader* shader, const char* name) { + return map_get(&shader->blockMap, hash64(name, strlen(name))) != MAP_NIL; +} + +const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) { + uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); + return index == MAP_NIL ? NULL : &shader->uniforms.data[index]; +} + +static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) { + uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); + if (index == MAP_NIL) { + return; + } + + Uniform* uniform = &shader->uniforms.data[index]; + lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name); + lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size); + + void* dest = uniform->value.bytes + start * size; + if (memcmp(dest, data, count * size)) { + lovrGraphicsFlushShader(shader); + memcpy(dest, data, count * size); + uniform->dirty = true; + } +} + +void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) { + lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, start, count, sizeof(float), "float"); +} + +void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count) { + lovrShaderSetUniform(shader, name, UNIFORM_INT, data, start, count, sizeof(int), "int"); +} + +void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count) { + lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, start, count, sizeof(float), "float"); +} + +void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) { + lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture"); +} + +void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count) { + lovrShaderSetUniform(shader, name, UNIFORM_IMAGE, data, start, count, sizeof(Image), "image"); +} + +void lovrShaderSetColor(Shader* shader, const char* name, Color color) { + color.r = lovrMathGammaToLinear(color.r); + color.g = lovrMathGammaToLinear(color.g); + color.b = lovrMathGammaToLinear(color.b); + lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float"); +} + +void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) { + uint64_t id = map_get(&shader->blockMap, hash64(name, strlen(name))); + if (id == MAP_NIL) return; + + int type = id & 1; + int index = id >> 1; + UniformBlock* block = &shader->blocks[type].data[index]; + + if (block->source != buffer || block->offset != offset || block->size != size) { + lovrGraphicsFlushShader(shader); + lovrRetain(buffer); + lovrRelease(Buffer, block->source); + block->access = access; + block->source = buffer; + block->offset = offset; + block->size = size; + } +} + +// ShaderBlock + +// Calculates uniform size and byte offsets using std140 rules, returning the total buffer size +size_t lovrShaderComputeUniformLayout(arr_uniform_t* uniforms) { + size_t size = 0; + for (size_t i = 0; i < uniforms->length; i++) { + int align; + Uniform* uniform = &uniforms->data[i]; + if (uniform->count > 1 || uniform->type == UNIFORM_MATRIX) { + align = 16; + uniform->size = align * uniform->count * (uniform->type == UNIFORM_MATRIX ? uniform->components : 1); + } else { + align = (uniform->components + (uniform->components == 3)) * 4; + uniform->size = uniform->components * 4; + } + uniform->offset = (size + (align - 1)) & -align; + size = uniform->offset + uniform->size; + } + return size; +} + +ShaderBlock* lovrShaderBlockCreate(BlockType type, Buffer* buffer, arr_uniform_t* uniforms) { + ShaderBlock* block = lovrAlloc(ShaderBlock); + arr_init(&block->uniforms); + map_init(&block->uniformMap, uniforms->length); + + arr_append(&block->uniforms, uniforms->data, uniforms->length); + + for (size_t i = 0; i < block->uniforms.length; i++) { + Uniform* uniform = &block->uniforms.data[i]; + map_set(&block->uniformMap, hash64(uniform->name, strlen(uniform->name)), i); + } + + block->type = type; + block->buffer = buffer; + lovrRetain(buffer); + return block; +} + +void lovrShaderBlockDestroy(void* ref) { + ShaderBlock* block = ref; + lovrRelease(Buffer, block->buffer); + arr_free(&block->uniforms); + map_free(&block->uniformMap); +} + +BlockType lovrShaderBlockGetType(ShaderBlock* block) { + return block->type; +} + +char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length) { + + // Calculate + size_t size = 0; + size_t tab = 2; + size += 15; // "layout(std140) " + size += block->type == BLOCK_UNIFORM ? 7 : 6; // "uniform" || "buffer" + size += 1; // " " + size += strlen(blockName); + size += 3; // " {\n" + for (size_t i = 0; i < block->uniforms.length; i++) { + size += tab; + size += getUniformTypeLength(&block->uniforms.data[i]); + size += 1; // " " + size += strlen(block->uniforms.data[i].name); + size += 2; // ";\n" + } + size += 3; // "};\n" + + // Allocate + char* code = malloc(size + 1); + lovrAssert(code, "Out of memory"); + + // Concatenate + char* s = code; + s += sprintf(s, "layout(std140) %s %s {\n", block->type == BLOCK_UNIFORM ? "uniform" : "buffer", blockName); + for (size_t i = 0; i < block->uniforms.length; i++) { + const Uniform* uniform = &block->uniforms.data[i]; + if (uniform->count > 1) { + s += sprintf(s, " %s %s[%d];\n", getUniformTypeName(uniform), uniform->name, uniform->count); + } else { + s += sprintf(s, " %s %s;\n", getUniformTypeName(uniform), uniform->name); + } + } + s += sprintf(s, "};\n"); + *s = '\0'; + + *length = size; + return code; +} + +const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) { + uint64_t index = map_get(&block->uniformMap, hash64(name, strlen(name))); + return index == MAP_NIL ? NULL : &block->uniforms.data[index]; +} + +Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) { + return block->buffer; +} + // Mesh Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, Buffer* vertexBuffer, uint32_t vertexCount) { diff --git a/src/modules/graphics/opengl.h b/src/modules/graphics/opengl.h index 6afd22f40..6c874b55e 100644 --- a/src/modules/graphics/opengl.h +++ b/src/modules/graphics/opengl.h @@ -14,6 +14,3 @@ #define GPU_MESH_FIELDS \ uint32_t vao; \ uint32_t ibo; - -#define GPU_SHADER_FIELDS \ - uint32_t program; diff --git a/src/modules/graphics/shader.c b/src/modules/graphics/shader.c deleted file mode 100644 index 7571bd9d1..000000000 --- a/src/modules/graphics/shader.c +++ /dev/null @@ -1,259 +0,0 @@ -#include "graphics/shader.h" -#include "graphics/graphics.h" -#include "graphics/buffer.h" -#include "math/math.h" -#include "resources/shaders.h" -#include "core/hash.h" -#include "core/ref.h" -#include -#include -#include - -static size_t getUniformTypeLength(const Uniform* uniform) { - size_t size = 0; - - if (uniform->count > 1) { - size += 2 + floor(log10(uniform->count)) + 1; // "[count]" - } - - switch (uniform->type) { - case UNIFORM_MATRIX: size += 4; break; - case UNIFORM_FLOAT: size += uniform->components == 1 ? 5 : 4; break; - case UNIFORM_INT: size += uniform->components == 1 ? 3 : 5; break; - default: break; - } - - return size; -} - -static const char* getUniformTypeName(const Uniform* uniform) { - switch (uniform->type) { - case UNIFORM_FLOAT: - switch (uniform->components) { - case 1: return "float"; - case 2: return "vec2"; - case 3: return "vec3"; - case 4: return "vec4"; - } - break; - - case UNIFORM_INT: - switch (uniform->components) { - case 1: return "int"; - case 2: return "ivec2"; - case 3: return "ivec3"; - case 4: return "ivec4"; - } - break; - - case UNIFORM_MATRIX: - switch (uniform->components) { - case 2: return "mat2"; - case 3: return "mat3"; - case 4: return "mat4"; - } - break; - - default: break; - } - - lovrThrow("Unreachable"); - return ""; -} - -Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type, ShaderFlag* flags, uint32_t flagCount, bool multiview) { - switch (type) { - case SHADER_UNLIT: return lovrShaderInitGraphics(shader, NULL, -1, NULL, -1, flags, flagCount, multiview); - case SHADER_STANDARD: return lovrShaderInitGraphics(shader, lovrStandardVertexShader, -1, lovrStandardFragmentShader, -1, flags, flagCount, multiview); - case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, -1, lovrCubeFragmentShader, -1, flags, flagCount, multiview); - case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, -1, lovrPanoFragmentShader, -1, flags, flagCount, multiview); - case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, -1, lovrFontFragmentShader, -1, flags, flagCount, multiview); - case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, -1, NULL, -1, flags, flagCount, multiview); - default: lovrThrow("Unknown default shader type"); return NULL; - } -} - -ShaderType lovrShaderGetType(Shader* shader) { - return shader->type; -} - -int lovrShaderGetAttributeLocation(Shader* shader, const char* name) { - uint64_t location = map_get(&shader->attributes, hash64(name, strlen(name))); - return location == MAP_NIL ? -1 : (int) location; -} - -bool lovrShaderHasUniform(Shader* shader, const char* name) { - return map_get(&shader->uniformMap, hash64(name, strlen(name))) != MAP_NIL; -} - -bool lovrShaderHasBlock(Shader* shader, const char* name) { - return map_get(&shader->blockMap, hash64(name, strlen(name))) != MAP_NIL; -} - -const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) { - uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); - return index == MAP_NIL ? NULL : &shader->uniforms.data[index]; -} - -static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) { - uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); - if (index == MAP_NIL) { - return; - } - - Uniform* uniform = &shader->uniforms.data[index]; - lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name); - lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size); - - void* dest = uniform->value.bytes + start * size; - if (memcmp(dest, data, count * size)) { - lovrGraphicsFlushShader(shader); - memcpy(dest, data, count * size); - uniform->dirty = true; - } -} - -void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) { - lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, start, count, sizeof(float), "float"); -} - -void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count) { - lovrShaderSetUniform(shader, name, UNIFORM_INT, data, start, count, sizeof(int), "int"); -} - -void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count) { - lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, start, count, sizeof(float), "float"); -} - -void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) { - lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture"); -} - -void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count) { - lovrShaderSetUniform(shader, name, UNIFORM_IMAGE, data, start, count, sizeof(Image), "image"); -} - -void lovrShaderSetColor(Shader* shader, const char* name, Color color) { - color.r = lovrMathGammaToLinear(color.r); - color.g = lovrMathGammaToLinear(color.g); - color.b = lovrMathGammaToLinear(color.b); - lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float"); -} - -void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) { - uint64_t id = map_get(&shader->blockMap, hash64(name, strlen(name))); - if (id == MAP_NIL) return; - - int type = id & 1; - int index = id >> 1; - UniformBlock* block = &shader->blocks[type].data[index]; - - if (block->source != buffer || block->offset != offset || block->size != size) { - lovrGraphicsFlushShader(shader); - lovrRetain(buffer); - lovrRelease(Buffer, block->source); - block->access = access; - block->source = buffer; - block->offset = offset; - block->size = size; - } -} - -// ShaderBlock - -// Calculates uniform size and byte offsets using std140 rules, returning the total buffer size -size_t lovrShaderComputeUniformLayout(arr_uniform_t* uniforms) { - size_t size = 0; - for (size_t i = 0; i < uniforms->length; i++) { - int align; - Uniform* uniform = &uniforms->data[i]; - if (uniform->count > 1 || uniform->type == UNIFORM_MATRIX) { - align = 16; - uniform->size = align * uniform->count * (uniform->type == UNIFORM_MATRIX ? uniform->components : 1); - } else { - align = (uniform->components + (uniform->components == 3)) * 4; - uniform->size = uniform->components * 4; - } - uniform->offset = (size + (align - 1)) & -align; - size = uniform->offset + uniform->size; - } - return size; -} - -ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, arr_uniform_t* uniforms) { - arr_init(&block->uniforms); - map_init(&block->uniformMap, uniforms->length); - - arr_append(&block->uniforms, uniforms->data, uniforms->length); - - for (size_t i = 0; i < block->uniforms.length; i++) { - Uniform* uniform = &block->uniforms.data[i]; - map_set(&block->uniformMap, hash64(uniform->name, strlen(uniform->name)), i); - } - - block->type = type; - block->buffer = buffer; - lovrRetain(buffer); - return block; -} - -void lovrShaderBlockDestroy(void* ref) { - ShaderBlock* block = ref; - lovrRelease(Buffer, block->buffer); - arr_free(&block->uniforms); - map_free(&block->uniformMap); -} - -BlockType lovrShaderBlockGetType(ShaderBlock* block) { - return block->type; -} - -char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length) { - - // Calculate - size_t size = 0; - size_t tab = 2; - size += 15; // "layout(std140) " - size += block->type == BLOCK_UNIFORM ? 7 : 6; // "uniform" || "buffer" - size += 1; // " " - size += strlen(blockName); - size += 3; // " {\n" - for (size_t i = 0; i < block->uniforms.length; i++) { - size += tab; - size += getUniformTypeLength(&block->uniforms.data[i]); - size += 1; // " " - size += strlen(block->uniforms.data[i].name); - size += 2; // ";\n" - } - size += 3; // "};\n" - - // Allocate - char* code = malloc(size + 1); - lovrAssert(code, "Out of memory"); - - // Concatenate - char* s = code; - s += sprintf(s, "layout(std140) %s %s {\n", block->type == BLOCK_UNIFORM ? "uniform" : "buffer", blockName); - for (size_t i = 0; i < block->uniforms.length; i++) { - const Uniform* uniform = &block->uniforms.data[i]; - if (uniform->count > 1) { - s += sprintf(s, " %s %s[%d];\n", getUniformTypeName(uniform), uniform->name, uniform->count); - } else { - s += sprintf(s, " %s %s;\n", getUniformTypeName(uniform), uniform->name); - } - } - s += sprintf(s, "};\n"); - *s = '\0'; - - *length = size; - return code; -} - -const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) { - uint64_t index = map_get(&block->uniformMap, hash64(name, strlen(name))); - return index == MAP_NIL ? NULL : &block->uniforms.data[index]; -} - -Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) { - return block->buffer; -} diff --git a/src/modules/graphics/shader.h b/src/modules/graphics/shader.h index f3a407cb1..eb367ef1b 100644 --- a/src/modules/graphics/shader.h +++ b/src/modules/graphics/shader.h @@ -1,5 +1,4 @@ #include "graphics/texture.h" -#include "graphics/opengl.h" #include "core/arr.h" #include @@ -92,13 +91,6 @@ typedef struct Uniform { typedef arr_t(Uniform) arr_uniform_t; -typedef struct { - BlockType type; - arr_uniform_t uniforms; - map_t uniformMap; - struct Buffer* buffer; -} ShaderBlock; - typedef struct { arr_uniform_t uniforms; UniformAccess access; @@ -110,25 +102,12 @@ typedef struct { typedef arr_t(UniformBlock) arr_block_t; -typedef struct Shader { - ShaderType type; - arr_uniform_t uniforms; - arr_block_t blocks[2]; - map_t attributes; - map_t uniformMap; - map_t blockMap; - bool multiview; - GPU_SHADER_FIELDS -} Shader; - // Shader -Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, int vertexSourceLength, const char* fragmentSource, int fragmentSourceLength, ShaderFlag* flags, uint32_t flagCount, bool multiview); -Shader* lovrShaderInitCompute(Shader* shader, const char* source, int length, ShaderFlag* flags, uint32_t flagCount); -Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type, ShaderFlag* flags, uint32_t flagCount, bool multiview); -#define lovrShaderCreateGraphics(...) lovrShaderInitGraphics(lovrAlloc(Shader), __VA_ARGS__) -#define lovrShaderCreateCompute(...) lovrShaderInitCompute(lovrAlloc(Shader), __VA_ARGS__) -#define lovrShaderCreateDefault(...) lovrShaderInitDefault(lovrAlloc(Shader), __VA_ARGS__) +typedef struct Shader Shader; +Shader* lovrShaderCreateGraphics(const char* vertexSource, int vertexSourceLength, const char* fragmentSource, int fragmentSourceLength, ShaderFlag* flags, uint32_t flagCount, bool multiview); +Shader* lovrShaderCreateCompute(const char* source, int length, ShaderFlag* flags, uint32_t flagCount); +Shader* lovrShaderCreateDefault(DefaultShader type, ShaderFlag* flags, uint32_t flagCount, bool multiview); void lovrShaderDestroy(void* ref); ShaderType lovrShaderGetType(Shader* shader); int lovrShaderGetAttributeLocation(Shader* shader, const char* name); @@ -147,8 +126,8 @@ void lovrShaderSetBlock(Shader* shader, const char* name, struct Buffer* buffer, size_t lovrShaderComputeUniformLayout(arr_uniform_t* uniforms); -ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, struct Buffer* buffer, arr_uniform_t* uniforms); -#define lovrShaderBlockCreate(...) lovrShaderBlockInit(lovrAlloc(ShaderBlock), __VA_ARGS__) +typedef struct ShaderBlock ShaderBlock; +ShaderBlock* lovrShaderBlockCreate(BlockType type, struct Buffer* buffer, arr_uniform_t* uniforms); void lovrShaderBlockDestroy(void* ref); BlockType lovrShaderBlockGetType(ShaderBlock* block); char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length); From f8b8d427d7b0131ee8ae7f02b8b357a412106e65 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Feb 2020 23:18:54 -0800 Subject: [PATCH 21/44] Opaque Mesh; Opaque OpenGL; --- src/api/l_graphics_mesh.c | 103 ++++++++++++---------- src/modules/graphics/mesh.c | 115 ------------------------- src/modules/graphics/mesh.h | 32 ++----- src/modules/graphics/opengl.c | 155 +++++++++++++++++++++++++++++++++- src/modules/graphics/opengl.h | 16 ---- src/modules/headset/openxr.c | 2 +- 6 files changed, 218 insertions(+), 205 deletions(-) delete mode 100644 src/modules/graphics/mesh.c delete mode 100644 src/modules/graphics/opengl.h diff --git a/src/api/l_graphics_mesh.c b/src/api/l_graphics_mesh.c index 41ebc0a16..3610dd0bc 100644 --- a/src/api/l_graphics_mesh.c +++ b/src/api/l_graphics_mesh.c @@ -12,20 +12,22 @@ static int l_lovrMeshAttachAttributes(lua_State* L) { Mesh* other = luax_checktype(L, 2, Mesh); int instanceDivisor = luaL_optinteger(L, 3, 0); if (lua_isnoneornil(L, 4)) { - for (uint32_t i = 0; i < other->attributeCount; i++) { - MeshAttribute attachment = other->attributes[i]; - if (attachment.buffer != other->vertexBuffer) { + uint32_t count = lovrMeshGetAttributeCount(other); + for (uint32_t i = 0; i < count; i++) { + MeshAttribute attachment = *lovrMeshGetAttribute(other, i); + if (attachment.buffer != lovrMeshGetVertexBuffer(other)) { break; } attachment.divisor = instanceDivisor; - lovrMeshAttachAttribute(mesh, other->attributeNames[i], &attachment); + lovrMeshAttachAttribute(mesh, lovrMeshGetAttributeName(other, i), &attachment); } } else if (lua_istable(L, 4)) { int length = luax_len(L, 4); for (int i = 0; i < length; i++) { lua_rawgeti(L, 4, i + 1); const char* name = lua_tostring(L, -1); - const MeshAttribute* attribute = lovrMeshGetAttribute(other, name); + uint32_t index = lovrMeshGetAttributeIndex(other, name); + const MeshAttribute* attribute = lovrMeshGetAttribute(other, index); lovrAssert(attribute, "Tried to attach non-existent attribute %s", name); MeshAttribute attachment = *attribute; attachment.divisor = instanceDivisor; @@ -36,7 +38,8 @@ static int l_lovrMeshAttachAttributes(lua_State* L) { int top = lua_gettop(L); for (int i = 4; i <= top; i++) { const char* name = lua_tostring(L, i); - const MeshAttribute* attribute = lovrMeshGetAttribute(other, name); + uint32_t index = lovrMeshGetAttributeIndex(other, name); + const MeshAttribute* attribute = lovrMeshGetAttribute(other, index); lovrAssert(attribute, "Tried to attach non-existent attribute %s", name); MeshAttribute attachment = *attribute; attachment.divisor = instanceDivisor; @@ -51,12 +54,13 @@ static int l_lovrMeshDetachAttributes(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); if (lua_isuserdata(L, 2)) { Mesh* other = luax_checktype(L, 2, Mesh); - for (uint32_t i = 0; i < other->attributeCount; i++) { - const MeshAttribute* attachment = &other->attributes[i]; - if (attachment->buffer != other->vertexBuffer) { + uint32_t count = lovrMeshGetAttributeCount(other); + for (uint32_t i = 0; i < count; i++) { + const MeshAttribute* attachment = lovrMeshGetAttribute(other, i); + if (attachment->buffer != lovrMeshGetVertexBuffer(other)) { break; } - lovrMeshDetachAttribute(mesh, other->attributeNames[i]); + lovrMeshDetachAttribute(mesh, lovrMeshGetAttributeName(other, i)); } } else if (lua_istable(L, 2)) { int length = luax_len(L, 2); @@ -98,14 +102,15 @@ static int l_lovrMeshSetDrawMode(lua_State* L) { static int l_lovrMeshGetVertexFormat(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); - lua_createtable(L, mesh->attributeCount, 0); - for (uint32_t i = 0; i < mesh->attributeCount; i++) { - const MeshAttribute* attribute = &mesh->attributes[i]; - if (attribute->buffer != mesh->vertexBuffer) { + uint32_t attributeCount = lovrMeshGetAttributeCount(mesh); + lua_createtable(L, attributeCount, 0); + for (uint32_t i = 0; i < attributeCount; i++) { + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, i); + if (attribute->buffer != lovrMeshGetVertexBuffer(mesh)) { break; } lua_createtable(L, 3, 0); - lua_pushstring(L, mesh->attributeNames[i]); + lua_pushstring(L, lovrMeshGetAttributeName(mesh, i)); lua_rawseti(L, -2, 1); luax_pushenum(L, AttributeTypes, attribute->type); lua_rawseti(L, -2, 2); @@ -125,18 +130,21 @@ static int l_lovrMeshGetVertexCount(lua_State* L) { static int l_lovrMeshGetVertex(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); int index = luaL_checkinteger(L, 2) - 1; + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); + uint32_t attributeCount = lovrMeshGetAttributeCount(mesh); + const MeshAttribute* firstAttribute = lovrMeshGetAttribute(mesh, 0); - if (!mesh->vertexBuffer || mesh->attributeCount == 0 || mesh->attributes[0].buffer != mesh->vertexBuffer) { + if (!buffer || attributeCount == 0 || firstAttribute->buffer != buffer) { lovrThrow("Mesh does not have a vertex buffer"); } - lovrAssert(lovrBufferIsReadable(mesh->vertexBuffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); - AttributeData data = { .raw = lovrBufferMap(mesh->vertexBuffer, index * mesh->attributes[0].stride) }; + lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); + AttributeData data = { .raw = lovrBufferMap(buffer, index * firstAttribute->stride) }; int components = 0; - for (uint32_t i = 0; i < mesh->attributeCount; i++) { - const MeshAttribute* attribute = &mesh->attributes[i]; - if (attribute->buffer != mesh->vertexBuffer) { + for (uint32_t i = 0; i < attributeCount; i++) { + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, i); + if (attribute->buffer != buffer) { break; } for (unsigned j = 0; j < attribute->components; j++, components++) { @@ -156,20 +164,23 @@ static int l_lovrMeshGetVertex(lua_State* L) { static int l_lovrMeshSetVertex(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); uint32_t index = luaL_checkinteger(L, 2) - 1; lovrAssert(index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1); bool table = lua_istable(L, 3); + uint32_t attributeCount = lovrMeshGetAttributeCount(mesh); + const MeshAttribute* firstAttribute = lovrMeshGetAttribute(mesh, 0); - if (!mesh->vertexBuffer || mesh->attributeCount == 0 || mesh->attributes[0].buffer != mesh->vertexBuffer) { + if (!buffer || attributeCount == 0 || firstAttribute->buffer != buffer) { lovrThrow("Mesh does not have a vertex buffer"); } - size_t stride = mesh->attributes[0].stride; - AttributeData data = { .raw = lovrBufferMap(mesh->vertexBuffer, index * stride) }; + size_t stride = firstAttribute->stride; + AttributeData data = { .raw = lovrBufferMap(buffer, index * stride) }; int component = 0; - for (uint32_t i = 0; i < mesh->attributeCount; i++) { - const MeshAttribute* attribute = &mesh->attributes[i]; - if (attribute->buffer != mesh->vertexBuffer) { + for (uint32_t i = 0; i < attributeCount; i++) { + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, i); + if (attribute->buffer != buffer) { break; } @@ -195,7 +206,7 @@ static int l_lovrMeshSetVertex(lua_State* L) { } } } - lovrBufferFlush(mesh->vertexBuffer, index * stride, stride); + lovrBufferFlush(buffer, index * stride, stride); return 0; } @@ -206,9 +217,8 @@ static int l_lovrMeshGetVertexAttribute(lua_State* L) { Buffer* buffer = lovrMeshGetVertexBuffer(mesh); lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); lovrAssert(vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); - lovrAssert(attributeIndex < mesh->attributeCount, "Invalid mesh attribute: %d", attributeIndex + 1); - lovrAssert(mesh->attributes[attributeIndex].buffer == mesh->vertexBuffer, "Invalid mesh attribute: %d", attributeIndex + 1); - MeshAttribute* attribute = &mesh->attributes[attributeIndex]; + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, attributeIndex); + lovrAssert(attribute && attribute->buffer == buffer, "Invalid mesh attribute: %d", attributeIndex + 1); AttributeData data = { .raw = lovrBufferMap(buffer, vertexIndex * attribute->stride + attribute->offset) }; for (unsigned i = 0; i < attribute->components; i++) { switch (attribute->type) { @@ -226,14 +236,14 @@ static int l_lovrMeshGetVertexAttribute(lua_State* L) { static int l_lovrMeshSetVertexAttribute(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); uint32_t vertexIndex = luaL_checkinteger(L, 2) - 1; uint32_t attributeIndex = luaL_checkinteger(L, 3) - 1; bool table = lua_istable(L, 4); lovrAssert(vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); - lovrAssert(attributeIndex < mesh->attributeCount, "Invalid mesh attribute: %d", attributeIndex + 1); - lovrAssert(mesh->attributes[attributeIndex].buffer == mesh->vertexBuffer, "Invalid mesh attribute: %d", attributeIndex + 1); - MeshAttribute* attribute = &mesh->attributes[attributeIndex]; - AttributeData data = { .raw = lovrBufferMap(mesh->vertexBuffer, vertexIndex * attribute->stride + attribute->offset) }; + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, attributeIndex); + lovrAssert(attribute && attribute->buffer == buffer, "Invalid mesh attribute: %d", attributeIndex + 1); + AttributeData data = { .raw = lovrBufferMap(buffer, vertexIndex * attribute->stride + attribute->offset) }; for (unsigned i = 0; i < attribute->components; i++) { int index = 4 + i; if (table) { @@ -265,29 +275,32 @@ static int l_lovrMeshSetVertexAttribute(lua_State* L) { case U32: attributeSize = attribute->components * sizeof(uint32_t); break; case F32: attributeSize = attribute->components * sizeof(float); break; } - lovrBufferFlush(mesh->vertexBuffer, vertexIndex * attribute->stride + attribute->offset, attributeSize); + lovrBufferFlush(buffer, vertexIndex * attribute->stride + attribute->offset, attributeSize); return 0; } static int l_lovrMeshSetVertices(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); + Buffer* buffer = lovrMeshGetVertexBuffer(mesh); + uint32_t attributeCount = lovrMeshGetAttributeCount(mesh); + const MeshAttribute* firstAttribute = lovrMeshGetAttribute(mesh, 0); - if (!mesh->vertexBuffer || mesh->attributeCount == 0 || mesh->attributes[0].buffer != mesh->vertexBuffer) { + if (!buffer || attributeCount == 0 || firstAttribute->buffer != buffer) { lovrThrow("Mesh:setVertices does not work when the Mesh does not have a vertex buffer"); } uint32_t capacity = lovrMeshGetVertexCount(mesh); uint32_t start = luaL_optinteger(L, 3, 1) - 1; uint32_t count = luaL_optinteger(L, 4, capacity - start); - size_t stride = mesh->attributes[0].stride; + size_t stride = firstAttribute->stride; Blob* blob = luax_totype(L, 2, Blob); if (blob) { count = MIN(count, blob->size / stride); lovrAssert(start + count <= capacity, "Overflow in Mesh:setVertices: Mesh can only hold %d vertices", capacity); - void* data = lovrBufferMap(mesh->vertexBuffer, start * stride); + void* data = lovrBufferMap(buffer, start * stride); memcpy(data, blob->data, count * stride); - lovrBufferFlush(mesh->vertexBuffer, start * stride, count * stride); + lovrBufferFlush(buffer, start * stride, count * stride); return 0; } @@ -295,15 +308,15 @@ static int l_lovrMeshSetVertices(lua_State* L) { count = MIN(count, lua_objlen(L, 2)); lovrAssert(start + count <= capacity, "Overflow in Mesh:setVertices: Mesh can only hold %d vertices", capacity); - AttributeData data = { .raw = lovrBufferMap(mesh->vertexBuffer, start * stride) }; + AttributeData data = { .raw = lovrBufferMap(buffer, start * stride) }; for (uint32_t i = 0; i < count; i++) { lua_rawgeti(L, 2, i + 1); luaL_checktype(L, -1, LUA_TTABLE); int component = 0; - for (uint32_t j = 0; j < mesh->attributeCount; j++) { - const MeshAttribute* attribute = &mesh->attributes[j]; - if (attribute->buffer != mesh->vertexBuffer) { + for (uint32_t j = 0; j < attributeCount; j++) { + const MeshAttribute* attribute = lovrMeshGetAttribute(mesh, i); + if (attribute->buffer != buffer) { break; } @@ -326,7 +339,7 @@ static int l_lovrMeshSetVertices(lua_State* L) { lua_pop(L, 1); } - lovrBufferFlush(mesh->vertexBuffer, start * stride, count * stride); + lovrBufferFlush(buffer, start * stride, count * stride); return 0; } diff --git a/src/modules/graphics/mesh.c b/src/modules/graphics/mesh.c deleted file mode 100644 index 0b057baa5..000000000 --- a/src/modules/graphics/mesh.c +++ /dev/null @@ -1,115 +0,0 @@ -#include "graphics/mesh.h" -#include "graphics/buffer.h" -#include "graphics/graphics.h" -#include "graphics/material.h" -#include "core/hash.h" -#include "core/ref.h" -#include - -Buffer* lovrMeshGetVertexBuffer(Mesh* mesh) { - return mesh->vertexBuffer; -} - -Buffer* lovrMeshGetIndexBuffer(Mesh* mesh) { - return mesh->indexBuffer; -} - -uint32_t lovrMeshGetVertexCount(Mesh* mesh) { - return mesh->vertexCount; -} - -uint32_t lovrMeshGetIndexCount(Mesh* mesh) { - return mesh->indexCount; -} - -size_t lovrMeshGetIndexSize(Mesh* mesh) { - return mesh->indexSize; -} - -void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) { - uint64_t hash = hash64(name, strlen(name)); - lovrAssert(map_get(&mesh->attributeMap, hash) == MAP_NIL, "Mesh already has an attribute named '%s'", name); - lovrAssert(mesh->attributeCount < MAX_ATTRIBUTES, "Mesh already has the max number of attributes (%d)", MAX_ATTRIBUTES); - lovrAssert(strlen(name) < MAX_ATTRIBUTE_NAME_LENGTH, "Mesh attribute name '%s' is too long (max is %d)", name, MAX_ATTRIBUTE_NAME_LENGTH); - lovrGraphicsFlushMesh(mesh); - uint64_t index = mesh->attributeCount++; - mesh->attributes[index] = *attribute; - strcpy(mesh->attributeNames[index], name); - map_set(&mesh->attributeMap, hash, index); - lovrRetain(attribute->buffer); -} - -void lovrMeshDetachAttribute(Mesh* mesh, const char* name) { - uint64_t hash = hash64(name, strlen(name)); - uint64_t index = map_get(&mesh->attributeMap, hash); - lovrAssert(index != MAP_NIL, "No attached attribute named '%s' was found", name); - MeshAttribute* attribute = &mesh->attributes[index]; - lovrGraphicsFlushMesh(mesh); - lovrRelease(Buffer, attribute->buffer); - map_remove(&mesh->attributeMap, hash); - mesh->attributeNames[index][0] = '\0'; - memmove(mesh->attributeNames + index, mesh->attributeNames + index + 1, (mesh->attributeCount - index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char)); - memmove(mesh->attributes + index, mesh->attributes + index + 1, (mesh->attributeCount - index - 1) * sizeof(MeshAttribute)); - mesh->attributeCount--; - for (uint32_t i = 0; i < MAX_ATTRIBUTES; i++) { - if (mesh->locations[i] > index) { - mesh->locations[i]--; - } else if (mesh->locations[i] == index) { - mesh->locations[i] = 0xff; - } - } -} - -const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) { - uint64_t hash = hash64(name, strlen(name)); - uint64_t index = map_get(&mesh->attributeMap, hash); - return index == MAP_NIL ? NULL : &mesh->attributes[index]; -} - -bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { - uint64_t hash = hash64(name, strlen(name)); - uint64_t index = map_get(&mesh->attributeMap, hash); - lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); - return !mesh->attributes[index].disabled; -} - -void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) { - bool disable = !enable; - uint64_t hash = hash64(name, strlen(name)); - uint64_t index = map_get(&mesh->attributeMap, hash); - lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); - if (mesh->attributes[index].disabled != disable) { - lovrGraphicsFlushMesh(mesh); - mesh->attributes[index].disabled = disable; - } -} - -DrawMode lovrMeshGetDrawMode(Mesh* mesh) { - return mesh->mode; -} - -void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) { - mesh->mode = mode; -} - -void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count) { - *start = mesh->drawStart; - *count = mesh->drawCount; -} - -void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count) { - uint32_t limit = mesh->indexSize > 0 ? mesh->indexCount : mesh->vertexCount; - lovrAssert(start + count <= limit, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1); - mesh->drawStart = start; - mesh->drawCount = count; -} - -Material* lovrMeshGetMaterial(Mesh* mesh) { - return mesh->material; -} - -void lovrMeshSetMaterial(Mesh* mesh, Material* material) { - lovrRetain(material); - lovrRelease(Material, mesh->material); - mesh->material = material; -} diff --git a/src/modules/graphics/mesh.h b/src/modules/graphics/mesh.h index 5a0b150e6..1d033af20 100644 --- a/src/modules/graphics/mesh.h +++ b/src/modules/graphics/mesh.h @@ -1,6 +1,4 @@ #include "data/modelData.h" -#include "graphics/opengl.h" -#include "core/map.h" #include #pragma once @@ -23,29 +21,8 @@ typedef struct { unsigned disabled : 1; } MeshAttribute; -typedef struct Mesh { - DrawMode mode; - char attributeNames[MAX_ATTRIBUTES][MAX_ATTRIBUTE_NAME_LENGTH]; - MeshAttribute attributes[MAX_ATTRIBUTES]; - uint8_t locations[MAX_ATTRIBUTES]; - uint16_t enabledLocations; - uint16_t divisors[MAX_ATTRIBUTES]; - map_t attributeMap; - uint32_t attributeCount; - struct Buffer* vertexBuffer; - struct Buffer* indexBuffer; - uint32_t vertexCount; - uint32_t indexCount; - size_t indexSize; - size_t indexOffset; - uint32_t drawStart; - uint32_t drawCount; - struct Material* material; - GPU_MESH_FIELDS -} Mesh; - -Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, struct Buffer* vertexBuffer, uint32_t vertexCount); -#define lovrMeshCreate(...) lovrMeshInit(lovrAlloc(Mesh), __VA_ARGS__) +typedef struct Mesh Mesh; +Mesh* lovrMeshCreate(DrawMode mode, struct Buffer* vertexBuffer, uint32_t vertexCount); void lovrMeshDestroy(void* ref); struct Buffer* lovrMeshGetVertexBuffer(Mesh* mesh); struct Buffer* lovrMeshGetIndexBuffer(Mesh* mesh); @@ -53,9 +30,12 @@ void lovrMeshSetIndexBuffer(Mesh* mesh, struct Buffer* buffer, uint32_t indexCou uint32_t lovrMeshGetVertexCount(Mesh* mesh); uint32_t lovrMeshGetIndexCount(Mesh* mesh); size_t lovrMeshGetIndexSize(Mesh* mesh); +uint32_t lovrMeshGetAttributeCount(Mesh* mesh); void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute); void lovrMeshDetachAttribute(Mesh* mesh, const char* name); -const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name); +const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, uint32_t index); +uint32_t lovrMeshGetAttributeIndex(Mesh* mesh, const char* name); +const char* lovrMeshGetAttributeName(Mesh* mesh, uint32_t index); bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name); void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enabled); DrawMode lovrMeshGetDrawMode(Mesh* mesh); diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 45c8c8a6c..a521f664f 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1,4 +1,3 @@ -#include "graphics/opengl.h" #include "graphics/graphics.h" #include "graphics/buffer.h" #include "graphics/canvas.h" @@ -17,6 +16,15 @@ #include #include +#ifdef LOVR_WEBGL +#include +#include +#include +#include +#else +#include "lib/glad/glad.h" +#endif + // Types #define MAX_TEXTURES 16 @@ -99,6 +107,28 @@ struct Shader { bool multiview; }; +struct Mesh { + uint32_t vao; + uint32_t ibo; + DrawMode mode; + char attributeNames[MAX_ATTRIBUTES][MAX_ATTRIBUTE_NAME_LENGTH]; + MeshAttribute attributes[MAX_ATTRIBUTES]; + uint8_t locations[MAX_ATTRIBUTES]; + uint16_t enabledLocations; + uint16_t divisors[MAX_ATTRIBUTES]; + map_t attributeMap; + uint32_t attributeCount; + struct Buffer* vertexBuffer; + struct Buffer* indexBuffer; + uint32_t vertexCount; + uint32_t indexCount; + size_t indexSize; + size_t indexOffset; + uint32_t drawStart; + uint32_t drawCount; + struct Material* material; +}; + typedef enum { BARRIER_BLOCK, BARRIER_UNIFORM_TEXTURE, @@ -2797,7 +2827,8 @@ Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) { // Mesh -Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, Buffer* vertexBuffer, uint32_t vertexCount) { +Mesh* lovrMeshCreate(DrawMode mode, Buffer* vertexBuffer, uint32_t vertexCount) { + Mesh* mesh = lovrAlloc(Mesh); mesh->mode = mode; mesh->vertexBuffer = vertexBuffer; mesh->vertexCount = vertexCount; @@ -2832,3 +2863,123 @@ void lovrMeshSetIndexBuffer(Mesh* mesh, Buffer* buffer, uint32_t indexCount, siz mesh->indexOffset = offset; } } + +Buffer* lovrMeshGetVertexBuffer(Mesh* mesh) { + return mesh->vertexBuffer; +} + +Buffer* lovrMeshGetIndexBuffer(Mesh* mesh) { + return mesh->indexBuffer; +} + +uint32_t lovrMeshGetVertexCount(Mesh* mesh) { + return mesh->vertexCount; +} + +uint32_t lovrMeshGetIndexCount(Mesh* mesh) { + return mesh->indexCount; +} + +size_t lovrMeshGetIndexSize(Mesh* mesh) { + return mesh->indexSize; +} + +uint32_t lovrMeshGetAttributeCount(Mesh* mesh) { + return mesh->attributeCount; +} + +void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) { + uint64_t hash = hash64(name, strlen(name)); + lovrAssert(map_get(&mesh->attributeMap, hash) == MAP_NIL, "Mesh already has an attribute named '%s'", name); + lovrAssert(mesh->attributeCount < MAX_ATTRIBUTES, "Mesh already has the max number of attributes (%d)", MAX_ATTRIBUTES); + lovrAssert(strlen(name) < MAX_ATTRIBUTE_NAME_LENGTH, "Mesh attribute name '%s' is too long (max is %d)", name, MAX_ATTRIBUTE_NAME_LENGTH); + lovrGraphicsFlushMesh(mesh); + uint64_t index = mesh->attributeCount++; + mesh->attributes[index] = *attribute; + strcpy(mesh->attributeNames[index], name); + map_set(&mesh->attributeMap, hash, index); + lovrRetain(attribute->buffer); +} + +void lovrMeshDetachAttribute(Mesh* mesh, const char* name) { + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "No attached attribute named '%s' was found", name); + MeshAttribute* attribute = &mesh->attributes[index]; + lovrGraphicsFlushMesh(mesh); + lovrRelease(Buffer, attribute->buffer); + map_remove(&mesh->attributeMap, hash); + mesh->attributeNames[index][0] = '\0'; + memmove(mesh->attributeNames + index, mesh->attributeNames + index + 1, (mesh->attributeCount - index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char)); + memmove(mesh->attributes + index, mesh->attributes + index + 1, (mesh->attributeCount - index - 1) * sizeof(MeshAttribute)); + mesh->attributeCount--; + for (uint32_t i = 0; i < MAX_ATTRIBUTES; i++) { + if (mesh->locations[i] > index) { + mesh->locations[i]--; + } else if (mesh->locations[i] == index) { + mesh->locations[i] = 0xff; + } + } +} + +const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, uint32_t index) { + return index < mesh->attributeCount ? &mesh->attributes[index] : NULL; +} + +uint32_t lovrMeshGetAttributeIndex(Mesh* mesh, const char* name) { + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + return index == MAP_NIL ? ~0u : index; +} + +const char* lovrMeshGetAttributeName(Mesh* mesh, uint32_t index) { + return mesh->attributeNames[index]; +} + +bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); + return !mesh->attributes[index].disabled; +} + +void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) { + bool disable = !enable; + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); + if (mesh->attributes[index].disabled != disable) { + lovrGraphicsFlushMesh(mesh); + mesh->attributes[index].disabled = disable; + } +} + +DrawMode lovrMeshGetDrawMode(Mesh* mesh) { + return mesh->mode; +} + +void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) { + mesh->mode = mode; +} + +void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count) { + *start = mesh->drawStart; + *count = mesh->drawCount; +} + +void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count) { + uint32_t limit = mesh->indexSize > 0 ? mesh->indexCount : mesh->vertexCount; + lovrAssert(start + count <= limit, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1); + mesh->drawStart = start; + mesh->drawCount = count; +} + +Material* lovrMeshGetMaterial(Mesh* mesh) { + return mesh->material; +} + +void lovrMeshSetMaterial(Mesh* mesh, Material* material) { + lovrRetain(material); + lovrRelease(Material, mesh->material); + mesh->material = material; +} diff --git a/src/modules/graphics/opengl.h b/src/modules/graphics/opengl.h deleted file mode 100644 index 6c874b55e..000000000 --- a/src/modules/graphics/opengl.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef LOVR_WEBGL -#include -#include -#include -#include -#else -#include "lib/glad/glad.h" -#endif - -#include - -#pragma once - -#define GPU_MESH_FIELDS \ - uint32_t vao; \ - uint32_t ibo; diff --git a/src/modules/headset/openxr.c b/src/modules/headset/openxr.c index e332846ff..5fb44fc72 100644 --- a/src/modules/headset/openxr.c +++ b/src/modules/headset/openxr.c @@ -4,7 +4,6 @@ #include "graphics/graphics.h" #include "graphics/canvas.h" #include "graphics/texture.h" -#include "graphics/opengl.h" #include "core/ref.h" #include "core/util.h" #include @@ -14,6 +13,7 @@ #endif #define XR_USE_GRAPHICS_API_OPENGL +#define GL_SRGB8_ALPHA8 0x8C43 #include #include From ef7ebaef852d731c516cbfb771c5f134ab47ad4b Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 23 Feb 2020 00:46:54 -0800 Subject: [PATCH 22/44] core/gpu: vulkan feature detection; rm stats; --- src/core/gpu.h | 6 +----- src/core/gpu_gl.c | 4 ---- src/core/gpu_vk.c | 26 +++++++++++++++++++++----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index c25b7e25b..a0f36462a 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -309,6 +309,7 @@ typedef struct { typedef struct { bool anisotropy; + bool astc; bool dxt; } gpu_features; @@ -318,10 +319,6 @@ typedef struct { uint32_t framebufferSamples; } gpu_limits; -typedef struct { - uint32_t drawCalls; -} gpu_stats; - bool gpu_init(gpu_config* config); void gpu_destroy(void); void gpu_frame_wait(void); @@ -338,4 +335,3 @@ void gpu_draw_indirect_indexed(gpu_buffer* buffer, uint64_t offset, uint32_t dra void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z); void gpu_get_features(gpu_features* features); void gpu_get_limits(gpu_limits* limits); -void gpu_get_stats(gpu_stats* stats); diff --git a/src/core/gpu_gl.c b/src/core/gpu_gl.c index 946c58006..3c19f0e6f 100644 --- a/src/core/gpu_gl.c +++ b/src/core/gpu_gl.c @@ -352,10 +352,6 @@ void gpu_get_limits(gpu_limits* limits) { glGetIntegerv(GL_MAX_FRAMEBUFFER_SAMPLES, &limits->framebufferSamples); } -void gpu_get_stats(gpu_stats* stats) { - stats->drawCalls = 0; -} - // Buffer size_t gpu_sizeof_buffer(void) { diff --git a/src/core/gpu_vk.c b/src/core/gpu_vk.c index 9c9de91a6..9c329aa6b 100644 --- a/src/core/gpu_vk.c +++ b/src/core/gpu_vk.c @@ -23,6 +23,7 @@ X(vkDestroyDebugUtilsMessengerEXT)\ X(vkEnumeratePhysicalDevices)\ X(vkGetPhysicalDeviceProperties)\ + X(vkGetPhysicalDeviceFeatures)\ X(vkGetPhysicalDeviceMemoryProperties)\ X(vkGetPhysicalDeviceQueueFamilyProperties)\ X(vkCreateDevice)\ @@ -184,6 +185,7 @@ typedef struct { static struct { gpu_config config; + gpu_features features; void* library; VkInstance instance; VkDebugUtilsMessengerEXT messenger; @@ -368,6 +370,12 @@ bool gpu_init(gpu_config* config) { return false; } + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physicalDevice, &features); + state.features.anisotropy = features.samplerAnisotropy; + state.features.astc = features.textureCompressionASTC_LDR; + state.features.dxt = features.textureCompressionBC; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &state.memoryProperties); VkDeviceQueueCreateInfo queueInfo = { @@ -379,6 +387,18 @@ bool gpu_init(gpu_config* config) { VkDeviceCreateInfo deviceInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = &(VkPhysicalDeviceFeatures2) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &(VkPhysicalDeviceMultiviewFeatures) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + .multiview = VK_TRUE + }, + .features = { + .fullDrawIndexUint32 = VK_TRUE, + .multiDrawIndirect = VK_TRUE, + .shaderSampledImageArrayDynamicIndexing = VK_TRUE + } + }, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueInfo }; @@ -556,17 +576,13 @@ void gpu_compute(gpu_shader* shader, uint32_t x, uint32_t y, uint32_t z) { } void gpu_get_features(gpu_features* features) { - // + *features = state.features; } void gpu_get_limits(gpu_limits* limits) { // } -void gpu_get_stats(gpu_stats* stats) { - // -} - // Buffer size_t gpu_sizeof_buffer() { From fa771b04bd1627b14f331e1f44a546468f285cd7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 23 Feb 2020 01:01:34 -0800 Subject: [PATCH 23/44] Fix CMake; --- CMakeLists.txt | 5 ----- src/modules/graphics/opengl.c | 4 ++++ src/modules/graphics/texture.h | 1 + src/modules/headset/openvr.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20c0d6d0d..b95082515 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,16 +414,11 @@ endif() if(LOVR_ENABLE_GRAPHICS) add_definitions(-DLOVR_ENABLE_GRAPHICS) target_sources(lovr PRIVATE - src/modules/graphics/buffer.c - src/modules/graphics/canvas.c src/modules/graphics/font.c src/modules/graphics/graphics.c src/modules/graphics/material.c - src/modules/graphics/mesh.c src/modules/graphics/model.c src/modules/graphics/opengl.c - src/modules/graphics/shader.c - src/modules/graphics/texture.c src/api/l_graphics.c src/api/l_graphics_canvas.c src/api/l_graphics_font.c diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index a521f664f..8bd638d6d 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1776,6 +1776,10 @@ void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, uint32 } } +uint64_t lovrTextureGetId(Texture* texture) { + return texture->id; +} + uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap) { return MAX(texture->width >> mipmap, 1); } diff --git a/src/modules/graphics/texture.h b/src/modules/graphics/texture.h index 0dfbb5ebf..50c27da25 100644 --- a/src/modules/graphics/texture.h +++ b/src/modules/graphics/texture.h @@ -20,6 +20,7 @@ Texture* lovrTextureCreateFromHandle(uint32_t handle, TextureType type, uint32_t void lovrTextureDestroy(void* ref); void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint32_t depth, TextureFormat format); void lovrTextureReplacePixels(Texture* texture, struct TextureData* data, uint32_t x, uint32_t y, uint32_t slice, uint32_t mipmap); +uint64_t lovrTextureGetId(Texture* texture); uint32_t lovrTextureGetWidth(Texture* texture, uint32_t mipmap); uint32_t lovrTextureGetHeight(Texture* texture, uint32_t mipmap); uint32_t lovrTextureGetDepth(Texture* texture, uint32_t mipmap); diff --git a/src/modules/headset/openvr.c b/src/modules/headset/openvr.c index 4f29a9c03..b0e781a5b 100755 --- a/src/modules/headset/openvr.c +++ b/src/modules/headset/openvr.c @@ -673,7 +673,7 @@ static void openvr_renderTo(void (*callback)(void*), void* userdata) { // Submit const Attachment* attachments = lovrCanvasGetAttachments(state.canvas, NULL); - ptrdiff_t id = attachments[0].texture->id; + ptrdiff_t id = lovrTextureGetId(attachments[0].texture); Texture_t eyeTexture = { (void*) id, ETextureType_TextureType_OpenGL, EColorSpace_ColorSpace_Linear }; VRTextureBounds_t left = { 0.f, 0.f, .5f, 1.f }; VRTextureBounds_t right = { .5f, 0.f, 1.f, 1.f }; From 69dd0d46748d2a205eb20bd6bcb471f98f8bb4fa Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 26 Feb 2020 00:25:49 -0800 Subject: [PATCH 24/44] audio: rm paused and stopped states; rm rewind; - There is now just one "playing" state. - Instead of rewind, use :seek(0). Note that now there is no way to resume or rewind all tracked sources. This can be improved in the future if there's a need for it, probably using variadic or table-based variants of the audio module functions. --- src/api/l_audio.c | 12 ---- src/api/l_audio_source.c | 24 -------- src/modules/audio/audio.c | 22 ++----- src/modules/audio/audio.h | 2 - src/modules/audio/source.c | 121 +++++++++++-------------------------- 5 files changed, 41 insertions(+), 140 deletions(-) diff --git a/src/api/l_audio.c b/src/api/l_audio.c index f1a5e9b45..877d35096 100644 --- a/src/api/l_audio.c +++ b/src/api/l_audio.c @@ -165,16 +165,6 @@ static int l_lovrAudioPause(lua_State* L) { return 0; } -static int l_lovrAudioResume(lua_State* L) { - lovrAudioResume(); - return 0; -} - -static int l_lovrAudioRewind(lua_State* L) { - lovrAudioRewind(); - return 0; -} - static int l_lovrAudioSetDopplerEffect(lua_State* L) { float factor = luax_optfloat(L, 1, 1.f); float speedOfSound = luax_optfloat(L, 2, 343.29f); @@ -237,8 +227,6 @@ static const luaL_Reg lovrAudio[] = { { "newMicrophone", l_lovrAudioNewMicrophone }, { "newSource", l_lovrAudioNewSource }, { "pause", l_lovrAudioPause }, - { "resume", l_lovrAudioResume }, - { "rewind", l_lovrAudioRewind }, { "setDopplerEffect", l_lovrAudioSetDopplerEffect }, { "setOrientation", l_lovrAudioSetOrientation }, { "setPose", l_lovrAudioSetPose }, diff --git a/src/api/l_audio_source.c b/src/api/l_audio_source.c index c149e5f95..32fc31587 100644 --- a/src/api/l_audio_source.c +++ b/src/api/l_audio_source.c @@ -134,11 +134,6 @@ static int l_lovrSourceIsLooping(lua_State* L) { return 1; } -static int l_lovrSourceIsPaused(lua_State* L) { - lua_pushboolean(L, lovrSourceIsPaused(luax_checktype(L, 1, Source))); - return 1; -} - static int l_lovrSourceIsPlaying(lua_State* L) { lua_pushboolean(L, lovrSourceIsPlaying(luax_checktype(L, 1, Source))); return 1; @@ -149,11 +144,6 @@ static int l_lovrSourceIsRelative(lua_State* L) { return 1; } -static int l_lovrSourceIsStopped(lua_State* L) { - lua_pushboolean(L, lovrSourceIsStopped(luax_checktype(L, 1, Source))); - return 1; -} - static int l_lovrSourcePause(lua_State* L) { lovrSourcePause(luax_checktype(L, 1, Source)); return 0; @@ -166,16 +156,6 @@ static int l_lovrSourcePlay(lua_State* L) { return 0; } -static int l_lovrSourceResume(lua_State* L) { - lovrSourceResume(luax_checktype(L, 1, Source)); - return 0; -} - -static int l_lovrSourceRewind(lua_State* L) { - lovrSourceRewind(luax_checktype(L, 1, Source)); - return 0; -} - static int l_lovrSourceSeek(lua_State* L) { Source* source = luax_checktype(L, 1, Source); TimeUnit unit = luax_checkenum(L, 3, TimeUnits, "seconds", "TimeUnit"); @@ -306,14 +286,10 @@ const luaL_Reg lovrSource[] = { { "getVolume", l_lovrSourceGetVolume }, { "getVolumeLimits", l_lovrSourceGetVolumeLimits }, { "isLooping", l_lovrSourceIsLooping }, - { "isPaused", l_lovrSourceIsPaused }, { "isPlaying", l_lovrSourceIsPlaying }, { "isRelative", l_lovrSourceIsRelative }, - { "isStopped", l_lovrSourceIsStopped }, { "pause", l_lovrSourcePause }, { "play", l_lovrSourcePlay }, - { "resume", l_lovrSourceResume }, - { "rewind", l_lovrSourceRewind }, { "seek", l_lovrSourceSeek }, { "setCone", l_lovrSourceSetCone }, { "setFalloff", l_lovrSourceSetFalloff }, diff --git a/src/modules/audio/audio.c b/src/modules/audio/audio.c index a9598024b..3c0c06c9d 100644 --- a/src/modules/audio/audio.c +++ b/src/modules/audio/audio.c @@ -64,13 +64,13 @@ bool lovrAudioInit() { void lovrAudioDestroy() { if (!state.initialized) return; - alcMakeContextCurrent(NULL); - alcDestroyContext(state.context); - alcCloseDevice(state.device); for (size_t i = 0; i < state.sources.length; i++) { lovrRelease(Source, state.sources.data[i]); } arr_free(&state.sources); + alcMakeContextCurrent(NULL); + alcDestroyContext(state.context); + alcCloseDevice(state.device); memset(&state, 0, sizeof(state)); } @@ -83,7 +83,9 @@ void lovrAudioUpdate() { } uint32_t id = lovrSourceGetId(source); - bool isStopped = lovrSourceIsStopped(source); + ALenum sourceState; + alGetSourcei(id, AL_SOURCE_STATE, &sourceState); + bool isStopped = sourceState == AL_STOPPED; ALint processed; alGetSourcei(id, AL_BUFFERS_PROCESSED, &processed); @@ -163,18 +165,6 @@ void lovrAudioPause() { } } -void lovrAudioResume() { - for (size_t i = 0; i < state.sources.length; i++) { - lovrSourceResume(state.sources.data[i]); - } -} - -void lovrAudioRewind() { - for (size_t i = 0; i < state.sources.length; i++) { - lovrSourceRewind(state.sources.data[i]); - } -} - void lovrAudioSetDopplerEffect(float factor, float speedOfSound) { alDopplerFactor(factor); alSpeedOfSound(speedOfSound); diff --git a/src/modules/audio/audio.h b/src/modules/audio/audio.h index e5704f1d0..ff223cd25 100644 --- a/src/modules/audio/audio.h +++ b/src/modules/audio/audio.h @@ -22,8 +22,6 @@ float lovrAudioGetVolume(void); bool lovrAudioHas(struct Source* source); bool lovrAudioIsSpatialized(void); void lovrAudioPause(void); -void lovrAudioResume(void); -void lovrAudioRewind(void); void lovrAudioSetDopplerEffect(float factor, float speedOfSound); void lovrAudioSetOrientation(float* orientation); void lovrAudioSetPosition(float* position); diff --git a/src/modules/audio/source.c b/src/modules/audio/source.c index 39cca65f8..ebacf4e6c 100644 --- a/src/modules/audio/source.c +++ b/src/modules/audio/source.c @@ -21,7 +21,7 @@ struct Source { bool isLooping; }; -static ALenum lovrSourceGetState(Source* source) { +static ALenum getState(Source* source) { ALenum state; alGetSourcei(source->id, AL_SOURCE_STATE, &state); return state; @@ -135,12 +135,8 @@ bool lovrSourceIsLooping(Source* source) { return source->isLooping; } -bool lovrSourceIsPaused(Source* source) { - return lovrSourceGetState(source) == AL_PAUSED; -} - bool lovrSourceIsPlaying(Source* source) { - return lovrSourceGetState(source) == AL_PLAYING; + return getState(source) == AL_PLAYING; } bool lovrSourceIsRelative(Source* source) { @@ -149,71 +145,42 @@ bool lovrSourceIsRelative(Source* source) { return isRelative == AL_TRUE; } -bool lovrSourceIsStopped(Source* source) { - return lovrSourceGetState(source) == AL_STOPPED; -} - void lovrSourcePause(Source* source) { alSourcePause(source->id); } void lovrSourcePlay(Source* source) { - if (lovrSourceIsPlaying(source)) { - return; - } else if (lovrSourceIsPaused(source)) { - lovrSourceResume(source); - return; - } - - // There is no guarantee that lovrAudioUpdate is called AFTER the state of source becomes STOPPED but - // BEFORE user code calls source:play(). This means that some buffers may still be queued (but processed - // and completely finished playing). These must be unqueued before we can start using the source again. - ALint processed; - ALuint _unused[SOURCE_BUFFERS]; - alGetSourcei(lovrSourceGetId(source), AL_BUFFERS_PROCESSED, &processed); - alSourceUnqueueBuffers(source->id, processed, _unused); - - lovrSourceStream(source, source->buffers, SOURCE_BUFFERS); - alSourcePlay(source->id); -} - -void lovrSourceResume(Source* source) { - if (!lovrSourceIsPaused(source)) { - return; - } - - alSourcePlay(source->id); -} - -void lovrSourceRewind(Source* source) { - if (lovrSourceIsStopped(source)) { - return; - } - - bool wasPaused = lovrSourceIsPaused(source); - alSourceRewind(source->id); - lovrSourceStop(source); - lovrSourcePlay(source); - if (wasPaused) { - lovrSourcePause(source); + if (source->type == SOURCE_STATIC) { + if (getState(source) != AL_PLAYING) { + alSourcePlay(source->id); + } + } else { + switch (getState(source)) { + case AL_INITIAL: + case AL_STOPPED: + alSourcei(source->id, AL_BUFFER, AL_NONE); + lovrSourceStream(source, source->buffers, SOURCE_BUFFERS); + alSourcePlay(source->id); + break; + case AL_PAUSED: + alSourcePlay(source->id); + break; + case AL_PLAYING: + break; + } } } void lovrSourceSeek(Source* source, size_t sample) { - switch (source->type) { - case SOURCE_STATIC: - alSourcef(source->id, AL_SAMPLE_OFFSET, sample); - break; - - case SOURCE_STREAM: { - bool wasPaused = lovrSourceIsPaused(source); - lovrSourceStop(source); - lovrAudioStreamSeek(source->stream, sample); - lovrSourcePlay(source); - if (wasPaused) { - lovrSourcePause(source); - } - break; + if (source->type == SOURCE_STATIC) { + alSourcef(source->id, AL_SAMPLE_OFFSET, sample); + } else { + bool wasPaused = getState(source) == AL_PAUSED; + alSourceStop(source->id); + lovrAudioStreamSeek(source->stream, sample); + lovrSourcePlay(source); + if (wasPaused) { + lovrSourcePause(source); } } } @@ -272,30 +239,12 @@ void lovrSourceSetVolumeLimits(Source* source, float min, float max) { } void lovrSourceStop(Source* source) { - if (lovrSourceIsStopped(source)) { - return; - } - - switch (source->type) { - case SOURCE_STATIC: - alSourceStop(source->id); - break; - - case SOURCE_STREAM: { - - // Stop the source - alSourceStop(source->id); - alSourcei(source->id, AL_BUFFER, AL_NONE); - - // Empty the buffers - int count = 0; - alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count); - alSourceUnqueueBuffers(source->id, count, NULL); - - // Rewind the decoder - lovrAudioStreamRewind(source->stream); - break; - } + if (source->type == SOURCE_STATIC) { + alSourceStop(source->id); + } else { + alSourceStop(source->id); + alSourcei(source->id, AL_BUFFER, AL_NONE); + lovrAudioStreamRewind(source->stream); } } From 90b605f012d7b47567efa1af989380bce19226ea Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 26 Feb 2020 14:40:40 -0800 Subject: [PATCH 25/44] Add restart cookie; lovr.event.restart; arg.restart; --- src/api/l_event.c | 20 ++++++++++++++++++-- src/main.c | 15 +++++++++++++-- src/modules/event/event.h | 1 + src/modules/graphics/graphics.c | 2 +- src/resources/boot.lua | 6 +++--- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/api/l_event.c b/src/api/l_event.c index 36f8661b1..42a3755cd 100644 --- a/src/api/l_event.c +++ b/src/api/l_event.c @@ -22,6 +22,7 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant) { int type = lua_type(L, index); switch (type) { case LUA_TNIL: + case LUA_TNONE: variant->type = TYPE_NIL; break; @@ -99,10 +100,12 @@ static int nextEvent(lua_State* L) { case EVENT_QUIT: if (event.data.quit.restart) { lua_pushliteral(L, "restart"); + luax_pushvariant(L, &event.data.quit.cookie); + return 3; } else { lua_pushnumber(L, event.data.quit.exitCode); + return 2; } - return 2; case EVENT_FOCUS: lua_pushboolean(L, event.data.boolean.value); @@ -173,9 +176,10 @@ static int l_lovrEventQuit(lua_State* L) { EventData data; int argType = lua_type(L, 1); - if (argType == LUA_TSTRING && 0 == strcmp("restart", lua_tostring(L, 1))) { + if (argType == LUA_TSTRING && !strcmp("restart", lua_tostring(L, 1))) { data.quit.restart = true; data.quit.exitCode = 0; + luax_checkvariant(L, 2, &data.quit.cookie); } else if (argType == LUA_TNUMBER || lua_isnoneornil(L, 1)) { data.quit.restart = false; data.quit.exitCode = luaL_optint(L, 1, 0); @@ -189,12 +193,24 @@ static int l_lovrEventQuit(lua_State* L) { return 0; } +static int l_lovrEventRestart(lua_State* L) { + EventData data; + data.quit.exitCode = 0; + data.quit.restart = true; + luax_checkvariant(L, 1, &data.quit.cookie); + EventType type = EVENT_QUIT; + Event event = { .type = type, .data = data }; + lovrEventPush(event); + return 0; +} + static const luaL_Reg lovrEvent[] = { { "clear", l_lovrEventClear }, { "poll", l_lovrEventPoll }, { "pump", l_lovrEventPump }, { "push", l_lovrEventPush }, { "quit", l_lovrEventQuit }, + { "restart", l_lovrEventRestart }, { NULL, NULL } }; diff --git a/src/main.c b/src/main.c index 51834cc06..20c7380a3 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "resources/boot.lua.h" #include "api/api.h" +#include "event/event.h" #include "core/os.h" #include "core/util.h" #include @@ -62,6 +63,8 @@ int main(int argc, char** argv) { int status; bool restart; + Variant cookie; + cookie.type = TYPE_NIL; do { lovrPlatformSetTime(0.); @@ -88,6 +91,9 @@ int main(int argc, char** argv) { lua_pushliteral(L, "lovr"); lua_setfield(L, -2, "exe"); + luax_pushvariant(L, &cookie); + lua_setfield(L, -2, "restart"); + typedef enum { // What flag is being searched for? ARGFLAG_NONE, // Not processing a flag ARGFLAG_ROOT @@ -157,8 +163,13 @@ int main(int argc, char** argv) { lovrPlatformSleep(0.); } - restart = lua_type(T, -1) == LUA_TSTRING && !strcmp(lua_tostring(T, -1), "restart"); - status = lua_tonumber(T, -1); + restart = lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart"); + status = lua_tonumber(T, 1); + luax_checkvariant(T, 2, &cookie); + if (cookie.type == TYPE_OBJECT) { + cookie.type = TYPE_NIL; + memset(&cookie.value, 0, sizeof(cookie.value)); + } lua_close(L); } while (restart); diff --git a/src/modules/event/event.h b/src/modules/event/event.h index 64917434b..656d6d3b0 100644 --- a/src/modules/event/event.h +++ b/src/modules/event/event.h @@ -43,6 +43,7 @@ typedef struct Variant { typedef struct { bool restart; int exitCode; + Variant cookie; } QuitEvent; typedef struct { diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index e284eb846..4d0d4cd4f 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -162,7 +162,7 @@ static void gammaCorrect(Color* color) { } static void onCloseWindow(void) { - lovrEventPush((Event) { .type = EVENT_QUIT, .data.quit = { false, 0 } }); + lovrEventPush((Event) { .type = EVENT_QUIT, .data.quit = { .exitCode = 0 } }); } static void onResizeWindow(int width, int height) { diff --git a/src/resources/boot.lua b/src/resources/boot.lua index e65a99cfd..68c64bfa8 100644 --- a/src/resources/boot.lua +++ b/src/resources/boot.lua @@ -150,7 +150,7 @@ function lovr.run() lovr.event.pump() for name, a, b, c, d in lovr.event.poll() do if name == 'quit' and (not lovr.quit or not lovr.quit()) then - return a or 0 + return a or 0, b end if lovr.handlers[name] then lovr.handlers[name](a, b, c, d) end end @@ -284,8 +284,8 @@ return function() return 1 end - local ok, result = xpcall(continuation, onerror) - if result and ok then return result -- Result is value returned by function. Return it. + local ok, result, extra = xpcall(continuation, onerror) + if result and ok then return result, extra -- Result is value returned by function. Return it. elseif not ok then continuation = result end -- Result is value returned by error handler. Make it the new error handler. local externerror = coroutine.yield() -- Return control to C code From 0d098410b3ffbf25376aa49a32d9ab2cd1bd7f57 Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 27 Feb 2020 20:20:02 -0800 Subject: [PATCH 26/44] Shader:send doesn't error on unknown uniform; Instead it returns a boolean indicating if the send worked. --- src/api/l_graphics_shader.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/api/l_graphics_shader.c b/src/api/l_graphics_shader.c index 6599dcdfb..ca8fd128d 100644 --- a/src/api/l_graphics_shader.c +++ b/src/api/l_graphics_shader.c @@ -208,7 +208,10 @@ static int l_lovrShaderSend(lua_State* L) { Shader* shader = luax_checktype(L, 1, Shader); const char* name = luaL_checkstring(L, 2); const Uniform* uniform = lovrShaderGetUniform(shader, name); - lovrAssert(uniform, "Unknown shader variable '%s'", name); + if (!uniform) { + lua_pushboolean(L, false); + return 1; + } if (tempData.size < uniform->size) { tempData.size = uniform->size; @@ -223,7 +226,8 @@ static int l_lovrShaderSend(lua_State* L) { case UNIFORM_SAMPLER: lovrShaderSetTextures(shader, uniform->name, tempData.data, 0, uniform->count); break; case UNIFORM_IMAGE: lovrShaderSetImages(shader, uniform->name, tempData.data, 0, uniform->count); break; } - return 0; + lua_pushboolean(L, true); + return 1; } static int l_lovrShaderSendBlock(lua_State* L) { From 1d6b7619ea339612e7fecad6c954cf6143149424 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 1 Mar 2020 18:08:59 -0800 Subject: [PATCH 27/44] oculus_mobile: Add handModels to bridge; --- src/modules/headset/oculus_mobile_bridge.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/headset/oculus_mobile_bridge.h b/src/modules/headset/oculus_mobile_bridge.h index 994851906..fffeb9706 100644 --- a/src/modules/headset/oculus_mobile_bridge.h +++ b/src/modules/headset/oculus_mobile_bridge.h @@ -2,6 +2,7 @@ #pragma once #include "../../core/util.h" +#include "data/modelData.h" #include // What's going on here: @@ -133,6 +134,7 @@ typedef struct { BridgeLovrVibrateFunction* vibrateFunction; // Returns true on success unsigned int textureHandles[4]; unsigned int textureCount; + ModelData* handModels[2]; } BridgeLovrInitData; LOVR_EXPORT void bridgeLovrInit(BridgeLovrInitData *initData); From 45648baa1c473e9ca54531108fb379edcf48198b Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 2 Mar 2020 22:23:59 -0800 Subject: [PATCH 28/44] Scope a variable better; --- src/modules/graphics/opengl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 8bd638d6d..5e9e40873 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1665,7 +1665,6 @@ void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint return; } - GLenum glFormat = convertTextureFormat(format); GLenum internalFormat = convertTextureFormatInternal(format, texture->srgb); #ifdef LOVR_GL if (GLAD_GL_ARB_texture_storage) { @@ -1677,6 +1676,7 @@ void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint } #ifdef LOVR_GL } else { + GLenum glFormat = convertTextureFormat(format); for (uint32_t i = 0; i < texture->mipmapCount; i++) { switch (texture->type) { case TEXTURE_2D: From 9c044ee91ad5c16849f1ba349e067c9b3cb83d07 Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 2 Mar 2020 22:24:14 -0800 Subject: [PATCH 29/44] CMake: Don't exclude Lua lib targets; --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b95082515..4c4dfa417 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ else() add_subdirectory(deps/lua lua) include_directories(deps/lua/src ${CMAKE_BINARY_DIR}/lua) set(LOVR_LUA liblua_static) - set_target_properties(lua luac liblua liblua_static PROPERTIES EXCLUDE_FROM_ALL 1) + set_target_properties(lua luac PROPERTIES EXCLUDE_FROM_ALL 1) endif() endif() From 8dcc97745e668472dfd901ad051c90d19036845b Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 2 Mar 2020 23:06:42 -0800 Subject: [PATCH 30/44] Tup: emscripten support; LuaJIT flag; --- Tupfile | 2 +- Tuprules.tup | 50 +++++++++++++++++++++++++++------------ config/default | 3 +++ src/modules/audio/audio.c | 2 ++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Tupfile b/Tupfile index 766c1e98a..074dc71e6 100644 --- a/Tupfile +++ b/Tupfile @@ -70,5 +70,5 @@ SRC_@(GRAPHICS) += src/resources/shaders.c : foreach $(RES) $(RES_y) |> !xd |> %f.h : foreach $(SRC) $(SRC_y) $(SRC_yy) | src/resources/*.h |> !cc |> .obj/%B.o -: .obj/*.o |> !ld |> lovr +: .obj/*.o $(STATIC_LIBS) $(STATIC_LIBS_y) |> !ld |> lovr$(SUFFIX) | $(EXTRAS) $(EXTRAS_y) : foreach $(LIBS) |> !cp |> libs/%b diff --git a/Tuprules.tup b/Tuprules.tup index c9c7c6dfd..1cc686c2f 100644 --- a/Tuprules.tup +++ b/Tuprules.tup @@ -41,41 +41,62 @@ CFLAGS_@(WEBVR) += -DLOVR_USE_WEBVR CFLAGS_@(LEAP) += -DLOVR_USE_LEAP ## Platforms -# A little gnarly, but basically you can set CONFIG_PLATFORM to pick a specific one, otherwise -# we use the builtin TUP_PLATFORM which is the current OS (so it won't work with android/web). -# Also macosx gets converted to macos ifeq (@(PLATFORM),) PLATFORM = @(TUP_PLATFORM) else PLATFORM = @(PLATFORM) endif + ifeq ($(PLATFORM),macosx) PLATFORM = macos -endif - -ifeq ($(PLATFORM),macos) LDFLAGS += -lobjc - LDFLAGS += -pagezero_size 10000 -image_base 100000000 - ORIGIN = @executable_path + LDFLAGS_@(LUAJIT) += -pagezero_size 10000 -image_base 100000000 + LDFLAGS += -Wl,-rpath,@executable_path/libs SO = dylib endif + ifeq ($(PLATFORM),linux) LDFLAGS += -lm -lpthread - ORIGIN = \$ORIGIN + LDFLAGS += -Wl,-rpath,\$ORIGIN/libs SO = so endif +ifeq ($(PLATFORM),web) + SUFFIX = .html + EXTRAS = lovr.js lovr.wasm + LDFLAGS += -s USE_GLFW=3 + LDFLAGS += -s USE_WEBGL2=1 + CFLAGS_@(THREAD) += -s USE_PTHREADS=1 + LDFLAGS_@(THREAD) += -s USE_PTHREADS=1 + EXTRAS_@(THREAD) += lovr.worker.js +endif + ## Libraries +ifeq ($(PLATFORM),web) + CFLAGS += -I$(ROOT)/deps/lua/src -I$(ROOT)/build/lua + STATIC_LIBS += $(ROOT)/build/lua/liblua.a + + CFLAGS_@(DATA) += -I$(ROOT)/deps/msdfgen + STATIC_LIBS_@(DATA) += $(ROOT)/build/lib_msdfgen/libmsdfgen.a + + CFLAGS_@(PHYSICS) += -I$(ROOT)/deps/ode/include -I$(ROOT)/build/ode/include + STATIC_LIBS_@(PHYSICS) += $(ROOT)/build/ode/libode.a +else ifeq (@(CMAKE_DEPS),y) # enet CFLAGS_@(ENET) += -I$(ROOT)/deps/enet/include LDFLAGS_@(ENET) += -L$(ROOT)/build/enet -lenet - # LuaJIT - CFLAGS += -I$(ROOT)/deps/luajit/src - LDFLAGS += -L$(ROOT)/build/luajit/src -lluajit - LIBS += $(ROOT)/build/luajit/src/libluajit.$(SO) + # Lua + ifeq (@(LUAJIT),y) + CFLAGS += -I$(ROOT)/deps/luajit/src + LDFLAGS += -L$(ROOT)/build/luajit/src -lluajit + LIBS += $(ROOT)/build/luajit/src/libluajit.$(SO) + else + CFLAGS += -I$(ROOT)/deps/lua/src -I$(ROOT)/build/lua + LDFLAGS += -L$(ROOT)/build/lua -llua + endif # msdfgen CFLAGS_@(DATA) += -I$(ROOT)/deps/msdfgen @@ -102,10 +123,9 @@ ifeq (@(CMAKE_DEPS),y) LDFLAGS_@(AUDIO) += -L$(ROOT)/build/openal -lopenal LIBS_@(AUDIO) += $(ROOT)/build/openal/libopenal.*$(SO)* - # Configure rpath to look for libraries in a `libs` folder next to the executable - LDFLAGS += -Wl,-rpath,$(ORIGIN)/libs LIBS += $(LIBS_y) endif +endif CFLAGS += @(EXTRA_CFLAGS) LDFLAGS += @(EXTRA_LDFLAGS) diff --git a/config/default b/config/default index d1bb248f5..41ad58f7d 100644 --- a/config/default +++ b/config/default @@ -51,6 +51,9 @@ CONFIG_LEAP=n CONFIG_OCULUS_PATH= CONFIG_LEAP_PATH= +## LuaJIT +CONFIG_LUAJIT=y + ## OpenGL flavor # Can be GL, GLES, or WEBGL. Ideally this should be autodetected though. CONFIG_GL=GL diff --git a/src/modules/audio/audio.c b/src/modules/audio/audio.c index 3c0c06c9d..eb340f249 100644 --- a/src/modules/audio/audio.c +++ b/src/modules/audio/audio.c @@ -8,7 +8,9 @@ #include #include #include +#ifndef EMSCRIPTEN #include +#endif static struct { bool initialized; From a18121b5eedc09916f747492e3df0baacd05e33d Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 3 Mar 2020 19:27:58 -0800 Subject: [PATCH 31/44] tup: fully support web builds; --- .gitignore | 4 ++++ Tuprules.tup | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index fdc2b30ff..c67d5f2b9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,10 @@ config/* /debug.sh /db /lovr +/lovr.js +/lovr.worker.js +/lovr.html +/lovr.wasm build build-* /libs diff --git a/Tuprules.tup b/Tuprules.tup index 1cc686c2f..062fe818a 100644 --- a/Tuprules.tup +++ b/Tuprules.tup @@ -64,11 +64,22 @@ endif ifeq ($(PLATFORM),web) SUFFIX = .html EXTRAS = lovr.js lovr.wasm + EXTRAS_@(THREAD) += lovr.worker.js LDFLAGS += -s USE_GLFW=3 - LDFLAGS += -s USE_WEBGL2=1 + LDFLAGS += -s USE_WEBGL2 + LDFLAGS += -s FULL_ES2 + LDFLAGS += -s FULL_ES3 + LDFLAGS += -s GL_PREINITIALIZED_CONTEXT + LDFLAGS += -s FORCE_FILESYSTEM + LDFLAGS += -s EXPORTED_FUNCTIONS="[ + LDFLAGS += '_main','_webvr_onAnimationFrame', + LDFLAGS += '_mat4_set','_mat4_identity','_mat4_invert','_mat4_multiply','_mat4_rotateQuat', + LDFLAGS += '_mat4_transform','_mat4_transformDirection' + LDFLAGS += ]" + LDFLAGS += --js-library $(ROOT)/src/resources/webvr.js + LDFLAGS += --shell-file $(ROOT)/src/resources/lovr.html CFLAGS_@(THREAD) += -s USE_PTHREADS=1 LDFLAGS_@(THREAD) += -s USE_PTHREADS=1 - EXTRAS_@(THREAD) += lovr.worker.js endif ## Libraries @@ -82,7 +93,7 @@ ifeq ($(PLATFORM),web) CFLAGS_@(PHYSICS) += -I$(ROOT)/deps/ode/include -I$(ROOT)/build/ode/include STATIC_LIBS_@(PHYSICS) += $(ROOT)/build/ode/libode.a else -ifeq (@(CMAKE_DEPS),y) + ifeq (@(CMAKE_DEPS),y) # enet CFLAGS_@(ENET) += -I$(ROOT)/deps/enet/include @@ -124,7 +135,7 @@ ifeq (@(CMAKE_DEPS),y) LIBS_@(AUDIO) += $(ROOT)/build/openal/libopenal.*$(SO)* LIBS += $(LIBS_y) -endif + endif endif CFLAGS += @(EXTRA_CFLAGS) From 6f6de92d19975c248938e83bb56a94c08c6e3f6f Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 4 Mar 2020 22:47:24 -0800 Subject: [PATCH 32/44] Add preliminary WebXR backend; --- Tupfile | 1 + Tuprules.tup | 12 +- src/api/l_headset.c | 3 +- src/modules/headset/headset.c | 3 + src/modules/headset/headset.h | 4 +- src/modules/headset/webxr.c | 53 ++++++++ src/resources/boot.lua | 2 +- src/resources/lovr.html | 3 +- src/resources/webxr.js | 219 ++++++++++++++++++++++++++++++++++ 9 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 src/modules/headset/webxr.c create mode 100644 src/resources/webxr.js diff --git a/Tupfile b/Tupfile index 074dc71e6..d778de2db 100644 --- a/Tupfile +++ b/Tupfile @@ -27,6 +27,7 @@ SRC_@(HEADSET)@(OPENXR) += src/modules/headset/openxr.c SRC_@(HEADSET)@(OCULUS) += src/modules/headset/oculus.c SRC_@(HEADSET)@(VRAPI) += src/modules/headset/oculus_mobile.c SRC_@(HEADSET)@(WEBVR) += src/modules/headset/webvr.c +SRC_@(HEADSET)@(WEBXR) += src/modules/headset/webxr.c SRC_@(HEADSET)@(LEAP) += src/modules/headset/leap.c SRC_@(MATH) += src/modules/math/*.c SRC_@(PHYSICS) += src/modules/physics/*.c diff --git a/Tuprules.tup b/Tuprules.tup index 062fe818a..dd32efd3f 100644 --- a/Tuprules.tup +++ b/Tuprules.tup @@ -38,6 +38,7 @@ CFLAGS_@(OCULUS) += -DLOVR_USE_OCULUS CFLAGS_@(OCULUS) += -I@(OCULUS_PATH)/LibOVR/Include CFLAGS_@(VRAPI) += -DLOVR_USE_OCULUS_MOBILE CFLAGS_@(WEBVR) += -DLOVR_USE_WEBVR +CFLAGS_@(WEBXR) += -DLOVR_USE_WEBXR CFLAGS_@(LEAP) += -DLOVR_USE_LEAP ## Platforms @@ -72,11 +73,18 @@ ifeq ($(PLATFORM),web) LDFLAGS += -s GL_PREINITIALIZED_CONTEXT LDFLAGS += -s FORCE_FILESYSTEM LDFLAGS += -s EXPORTED_FUNCTIONS="[ - LDFLAGS += '_main','_webvr_onAnimationFrame', + LDFLAGS += '_main', + ifeq (@(WEBVR),y) + LDFLAGS += '_webvr_onAnimationFrame', + endif + ifeq (@(WEBXR),y) + LDFLAGS += '_lovrCanvasCreateFromHandle','_lovrGraphicsSetCamera', + endif LDFLAGS += '_mat4_set','_mat4_identity','_mat4_invert','_mat4_multiply','_mat4_rotateQuat', LDFLAGS += '_mat4_transform','_mat4_transformDirection' LDFLAGS += ]" - LDFLAGS += --js-library $(ROOT)/src/resources/webvr.js + LDFLAGS_@(WEBVR) += --js-library $(ROOT)/src/resources/webvr.js + LDFLAGS_@(WEBXR) += --js-library $(ROOT)/src/resources/webxr.js LDFLAGS += --shell-file $(ROOT)/src/resources/lovr.html CFLAGS_@(THREAD) += -s USE_PTHREADS=1 LDFLAGS_@(THREAD) += -s USE_PTHREADS=1 diff --git a/src/api/l_headset.c b/src/api/l_headset.c index 9d273f279..c62a09451 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -20,6 +20,7 @@ StringEntry HeadsetDrivers[] = { [DRIVER_OPENVR] = ENTRY("openvr"), [DRIVER_OPENXR] = ENTRY("openxr"), [DRIVER_WEBVR] = ENTRY("webvr"), + [DRIVER_WEBXR] = ENTRY("webxr"), { 0 } }; @@ -694,7 +695,7 @@ int luaopen_lovr_headset(lua_State* L) { lua_getfield(L, -1, "headset"); size_t driverCount = 0; - HeadsetDriver drivers[8]; + HeadsetDriver drivers[16]; float offset = 1.7f; int msaa = 4; diff --git a/src/modules/headset/headset.c b/src/modules/headset/headset.c index 6b09ba733..1b0894b97 100644 --- a/src/modules/headset/headset.c +++ b/src/modules/headset/headset.c @@ -35,6 +35,9 @@ bool lovrHeadsetInit(HeadsetDriver* drivers, size_t count, float offset, uint32_ #endif #ifdef LOVR_USE_WEBVR case DRIVER_WEBVR: interface = &lovrHeadsetWebVRDriver; break; +#endif +#ifdef LOVR_USE_WEBXR + case DRIVER_WEBXR: interface = &lovrHeadsetWebXRDriver; break; #endif default: continue; } diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 7f39c1e13..548614033 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -16,7 +16,8 @@ typedef enum { DRIVER_OCULUS_MOBILE, DRIVER_OPENVR, DRIVER_OPENXR, - DRIVER_WEBVR + DRIVER_WEBVR, + DRIVER_WEBXR } HeadsetDriver; typedef enum { @@ -112,6 +113,7 @@ extern HeadsetInterface lovrHeadsetOculusDriver; extern HeadsetInterface lovrHeadsetOpenVRDriver; extern HeadsetInterface lovrHeadsetOpenXRDriver; extern HeadsetInterface lovrHeadsetWebVRDriver; +extern HeadsetInterface lovrHeadsetWebXRDriver; extern HeadsetInterface lovrHeadsetDesktopDriver; extern HeadsetInterface lovrHeadsetOculusMobileDriver; extern HeadsetInterface lovrHeadsetLeapMotionDriver; diff --git a/src/modules/headset/webxr.c b/src/modules/headset/webxr.c new file mode 100644 index 000000000..fe259487e --- /dev/null +++ b/src/modules/headset/webxr.c @@ -0,0 +1,53 @@ +#include "headset/headset.h" +#include + +extern bool webxr_init(float offset, uint32_t msaa); +extern void webxr_destroy(void); +extern bool webxr_getName(char* name, size_t length); +extern HeadsetOrigin webxr_getOriginType(void); +extern double webxr_getDisplayTime(void); +extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height); +extern const float* webxr_getDisplayMask(uint32_t* count); +extern uint32_t webxr_getViewCount(void); +extern bool webxr_getViewPose(uint32_t view, float* position, float* orientation); +extern bool webxr_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down); +extern void webxr_getClipDistance(float* near, float* far); +extern void webxr_setClipDistance(float near, float far); +extern void webxr_getBoundsDimensions(float* width, float* depth); +extern const float* webxr_getBoundsGeometry(uint32_t* count); +extern bool webxr_getPose(Device device, float* position, float* orientation); +extern bool webxr_getVelocity(Device device, float* velocity, float* angularVelocity); +extern bool webxr_isDown(Device device, DeviceButton button, bool* down, bool* changed); +extern bool webxr_isTouched(Device device, DeviceButton button, bool* touched); +extern bool webxr_getAxis(Device device, DeviceAxis axis, float* value); +extern bool webxr_vibrate(Device device, float strength, float duration, float frequency); +extern struct ModelData* webxr_newModelData(Device device); +extern void webxr_renderTo(void (*callback)(void*), void* userdata); +extern void webxr_update(float dt); + +HeadsetInterface lovrHeadsetWebXRDriver = { + .driverType = DRIVER_WEBVR, + .init = webxr_init, + .destroy = webxr_destroy, + .getName = webxr_getName, + .getOriginType = webxr_getOriginType, + .getDisplayTime = webxr_getDisplayTime, + .getDisplayDimensions = webxr_getDisplayDimensions, + .getDisplayMask = webxr_getDisplayMask, + .getViewCount = webxr_getViewCount, + .getViewPose = webxr_getViewPose, + .getViewAngles = webxr_getViewAngles, + .getClipDistance = webxr_getClipDistance, + .setClipDistance = webxr_setClipDistance, + .getBoundsDimensions = webxr_getBoundsDimensions, + .getBoundsGeometry = webxr_getBoundsGeometry, + .getPose = webxr_getPose, + .getVelocity = webxr_getVelocity, + .isDown = webxr_isDown, + .isTouched = webxr_isTouched, + .getAxis = webxr_getAxis, + .vibrate = webxr_vibrate, + .newModelData = webxr_newModelData, + .renderTo = webxr_renderTo, + .update = webxr_update +}; diff --git a/src/resources/boot.lua b/src/resources/boot.lua index 68c64bfa8..e303690e0 100644 --- a/src/resources/boot.lua +++ b/src/resources/boot.lua @@ -96,7 +96,7 @@ function lovr.boot() timer = true }, headset = { - drivers = { 'leap', 'openxr', 'oculus', 'oculusmobile', 'openvr', 'webvr', 'desktop' }, + drivers = { 'leap', 'openxr', 'oculus', 'oculusmobile', 'openvr', 'webxr', 'webvr', 'desktop' }, offset = 1.7, msaa = 4 }, diff --git a/src/resources/lovr.html b/src/resources/lovr.html index 244ae1aac..187279490 100644 --- a/src/resources/lovr.html +++ b/src/resources/lovr.html @@ -51,7 +51,8 @@ antialias: true, depth: true, stencil: true, - preserveDrawingBuffer: true + preserveDrawingBuffer: true, + xrCompatible: true }); var Module = window.Module = { diff --git a/src/resources/webxr.js b/src/resources/webxr.js new file mode 100644 index 000000000..a76980001 --- /dev/null +++ b/src/resources/webxr.js @@ -0,0 +1,219 @@ +var webxr = { + $state: {}, + + webxr_init: function(offset, msaa) { + if (!navigator.xr) { + return false; + } + + state.layer = null; + state.frame = null; + state.canvas = null; + state.camera = null; + state.clipNear = .1; + state.clipFar = 1000.0; + state.referenceSpaceType = null; + state.renderCallback = null; + state.renderUserdata = null; + state.animationFrame = null; + state.displayTime = null; + + navigator.xr.requestSession('inline').then(function(session) { + state.referenceSpaceType = 'viewer'; + session.requestReferenceSpace(state.referenceSpaceType).then(function(referenceSpace) { + state.session = session; + state.layer = new XRWebGLLayer(session, Module.preinitializedWebGLContext); + + var framebuffer = 0; + + if (state.layer.framebuffer) { + framebuffer = GL.getNewId(GL.framebuffers); + GL.framebuffers[framebuffer] = state.layer.framebuffer; + } + + var sizeof_CanvasFlags = 16; + var flags = Module.stackAlloc(sizeof_CanvasFlags); + HEAPU8.fill(0, flags, flags + sizeof_CanvasFlags); // memset(&flags, 0, sizeof(CanvasFlags)); + var width = state.layer.framebufferWidth; + var height = state.layer.framebufferHeight; + state.canvas = Module['_lovrCanvasCreateFromHandle'](width, height, flags, framebuffer, 0, 0, 1, true); + Module.stackRestore(flags); + + var sizeof_Camera = 264; + state.camera = Module._malloc(sizeof_Camera); + HEAPU32[(state.camera + 4) >> 2] = state.canvas; // state.camera.canvas = state.canvas + + session.updateRenderState({ + baseLayer: state.layer + }); + + state.animationFrame = session.requestAnimationFrame(function onFrame(t, frame) { + state.animationFrame = session.requestAnimationFrame(onFrame); + state.displayTime = t; + state.frame = frame; + + var views = frame.getViewerPose(referenceSpace).views; + + var stereo = views.length > 1; + HEAPU8[state.camera + 0] = stereo; // camera.stereo = stereo + + var matrices = (state.camera + 8) >> 2; + HEAPF32.set(views[0].transform.inverse.matrix, matrices + 0); + HEAPF32.set(views[0].projectionMatrix, matrices + 32); + + if (stereo) { + HEAPF32.set(views[1].transform.inverse.matrix, matrices + 16); + HEAPF32.set(views[1].projectionMatrix, matrices + 48); + } + + Module['_lovrGraphicsSetCamera'](state.camera, true); + + if (state.renderCallback) { + Module['dynCall_vi'](state.renderCallback, state.renderUserdata); + } + + Module['_lovrGraphicsSetCamera'](0, false); + }); + }); + }); + + return true; + }, + + webxr_destroy: function() { + function cleanup() { + // TODO release canvas + Module._free(state.camera|0); + } + + if (state.session) { + state.session.cancelAnimationFrame(state.animationFrame); + state.session.end().then(cleanup); + } else { + cleanup(); + } + }, + + webxr_getName: function(name, size) { + return false; + }, + + webxr_getOriginType: function() { + if (state.referenceSpaceType === 'local-floor' || state.referenceSpaceType === 'bounded-floor') { + return 1; /* ORIGIN_FLOOR */ + } + + return 0; /* ORIGIN_HEAD */ + }, + + webxr_getDisplayTime: function() { + return state.displayTime; + }, + + webxr_getDisplayDimensions: function(width, height) { + HEAPU32[width >> 2] = state.layer.framebufferWidth; + HEAPU32[height >> 2] = state.layer.framebufferHeight; + }, + + webxr_getDisplayFrequency: function() { + return 0.0; + }, + + webxr_getDisplayMask: function() { + return 0; /* NULL */ + }, + + webxr_getViewCount: function() { + if (!state.frame) { + return 0; + } + + return getViewerPose(state.frame).views.length; + }, + + webxr_getViewPose: function(index, position, orientation) { + if (!state.frame) { + return false; + } + var view = getViewerPose(state.frame).views[index]; + if (view) { + HEAPF32[position >> 2 + 0] = view.transform.position.x; + HEAPF32[position >> 2 + 1] = view.transform.position.y; + HEAPF32[position >> 2 + 2] = view.transform.position.z; + HEAPF32[position >> 2 + 3] = view.transform.position.w; + HEAPF32[orientation >> 2 + 0] = view.transform.orientation.x; + HEAPF32[orientation >> 2 + 1] = view.transform.orientation.y; + HEAPF32[orientation >> 2 + 2] = view.transform.orientation.z; + HEAPF32[orientation >> 2 + 3] = view.transform.orientation.w; + return true; + } + return false; + }, + + webxr_getViewAngles: function(index, left, right, up, down) { + return false; + }, + + webxr_getClipDistance: function(clipNear, clipFar) { + HEAPF32[clipNear >> 2] = state.clipNear; + HEAPF32[clipFar >> 2] = state.clipFar; + }, + + webxr_setClipDistance: function(clipNear, clipFar) { + state.clipNear = clipNear; + state.clipFar = clipFar; + state.session.updateRenderState({ + clipNear: clipNear, + clipFar: clipFar + }); + }, + + webxr_getBoundsDimensions: function(width, depth) { + HEAPF32[width >> 2] = 0.0; + HEAPF32[depth >> 2] = 0.0; + }, + + webxr_getBoundsGeometry: function(count) { + return 0; /* NULL */ // TODO + }, + + webxr_getPose: function(device, position, orientation) { + return false; // TODO + }, + + webxr_getVelocity: function(device, velocity, angularVelocity) { + return false; // TODO + }, + + webxr_isDown: function(device, button, down, changed) { + return false; // TODO + }, + + webxr_isTouched: function(device, button, touched) { + return false; // TODO + }, + + webxr_getAxis: function(device, axis, value) { + return false; // TODO + }, + + webxr_vibrate: function(device, strength, duration, frequency) { + return false; // TODO + }, + + webxr_newModelData: function(device) { + return 0; /* NULL */ // TODO + }, + + webxr_renderTo: function(callback, userdata) { + state.renderCallback = callback; + state.renderUserdata = userdata; + }, + + webxr_update: function(dt) { + // + } +}; + +autoAddDeps(webxr, '$state'); +mergeInto(LibraryManager.library, webxr); From c02839f5c2487aa30511eb60d29e227ab8e54d47 Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 5 Mar 2020 09:46:49 -0800 Subject: [PATCH 33/44] Allow anisotropy to be used with any TextureFilter; Instead of anisotropic being its own filter, it is now removed and anisotropy settings can be used with any of the other filter modes. --- src/api/l_graphics.c | 1 - src/api/l_graphics_texture.c | 7 ++----- src/modules/data/modelData.h | 3 +-- src/modules/graphics/opengl.c | 4 +--- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index 8f3244582..291f4302e 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -111,7 +111,6 @@ StringEntry FilterModes[] = { [FILTER_NEAREST] = ENTRY("nearest"), [FILTER_BILINEAR] = ENTRY("bilinear"), [FILTER_TRILINEAR] = ENTRY("trilinear"), - [FILTER_ANISOTROPIC] = ENTRY("anisotropic"), { 0 } }; diff --git a/src/api/l_graphics_texture.c b/src/api/l_graphics_texture.c index 6de264384..38a07e0c2 100644 --- a/src/api/l_graphics_texture.c +++ b/src/api/l_graphics_texture.c @@ -35,11 +35,8 @@ static int l_lovrTextureGetFilter(lua_State* L) { Texture* texture = luax_checktype(L, 1, Texture); TextureFilter filter = lovrTextureGetFilter(texture); luax_pushenum(L, FilterModes, filter.mode); - if (filter.mode == FILTER_ANISOTROPIC) { - lua_pushnumber(L, filter.anisotropy); - return 2; - } - return 1; + lua_pushnumber(L, filter.anisotropy); + return 2; } static int l_lovrTextureGetFormat(lua_State* L) { diff --git a/src/modules/data/modelData.h b/src/modules/data/modelData.h index 26737412f..979713d42 100644 --- a/src/modules/data/modelData.h +++ b/src/modules/data/modelData.h @@ -35,8 +35,7 @@ typedef enum { typedef enum { FILTER_NEAREST, FILTER_BILINEAR, - FILTER_TRILINEAR, - FILTER_ANISOTROPIC + FILTER_TRILINEAR } FilterMode; typedef struct { diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 5e9e40873..49e66a33e 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1837,7 +1837,6 @@ void lovrTextureSetCompareMode(Texture* texture, CompareMode compareMode) { void lovrTextureSetFilter(Texture* texture, TextureFilter filter) { lovrGraphicsFlush(); - float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.f) : 1.f; lovrGpuBindTexture(texture, 0); texture->filter = filter; @@ -1858,7 +1857,6 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) { break; case FILTER_TRILINEAR: - case FILTER_ANISOTROPIC: if (texture->mipmaps) { glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -1869,7 +1867,7 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) { break; } - glTexParameteri(texture->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); + glTexParameteri(texture->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, MAX(filter.anisotropy, 1.f)); } void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) { From a01cc5a376a05398e498578ba5557afd737f8c1f Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 5 Mar 2020 19:27:08 -0800 Subject: [PATCH 34/44] Fix lovr.graphics.getDefaultFilter; --- src/api/l_graphics.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index 291f4302e..ca44c9b2a 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -585,11 +585,8 @@ static int l_lovrGraphicsSetCullingEnabled(lua_State* L) { static int l_lovrGraphicsGetDefaultFilter(lua_State* L) { TextureFilter filter = lovrGraphicsGetDefaultFilter(); luax_pushenum(L, FilterModes, filter.mode); - if (filter.mode == FILTER_ANISOTROPIC) { - lua_pushnumber(L, filter.anisotropy); - return 2; - } - return 1; + lua_pushnumber(L, filter.anisotropy); + return 2; } static int l_lovrGraphicsSetDefaultFilter(lua_State* L) { From 6d0369d3656bd5c7958d43841168bf1741502b05 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 7 Mar 2020 16:35:32 -0800 Subject: [PATCH 35/44] Convert some collider functions to use vectors; --- src/api/l_physics_collider.c | 43 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/api/l_physics_collider.c b/src/api/l_physics_collider.c index 01d27849b..f349109f9 100644 --- a/src/api/l_physics_collider.c +++ b/src/api/l_physics_collider.c @@ -201,10 +201,9 @@ static int l_lovrColliderGetPosition(lua_State* L) { static int l_lovrColliderSetPosition(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - float x = luax_checkfloat(L, 2); - float y = luax_checkfloat(L, 3); - float z = luax_checkfloat(L, 4); - lovrColliderSetPosition(collider, x, y, z); + float position[4]; + luax_readvec3(L, 2, position, NULL); + lovrColliderSetPosition(collider, position[0], position[1], position[2]); return 0; } @@ -221,11 +220,9 @@ static int l_lovrColliderGetOrientation(lua_State* L) { static int l_lovrColliderSetOrientation(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - float angle = luax_checkfloat(L, 2); - float x = luax_checkfloat(L, 3); - float y = luax_checkfloat(L, 4); - float z = luax_checkfloat(L, 5); - lovrColliderSetOrientation(collider, angle, x, y, z); + float orientation[4]; + luax_readquat(L, 2, orientation, NULL); + lovrColliderSetOrientation(collider, orientation[0], orientation[1], orientation[2], orientation[3]); return 0; } @@ -246,15 +243,11 @@ static int l_lovrColliderGetPose(lua_State* L) { static int l_lovrColliderSetPose(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - float x = luax_checkfloat(L, 2); - float y = luax_checkfloat(L, 3); - float z = luax_checkfloat(L, 4); - float angle = luax_checkfloat(L, 5); - float ax = luax_checkfloat(L, 6); - float ay = luax_checkfloat(L, 7); - float az = luax_checkfloat(L, 8); - lovrColliderSetPosition(collider, x, y, z); - lovrColliderSetOrientation(collider, angle, ax, ay, az); + float position[4], orientation[4]; + int index = luax_readvec3(L, 2, position, NULL); + luax_readquat(L, index, orientation, NULL); + lovrColliderSetPosition(collider, position[0], position[1], position[2]); + lovrColliderSetOrientation(collider, orientation[0], orientation[1], orientation[2], orientation[3]); return 0; } @@ -270,10 +263,9 @@ static int l_lovrColliderGetLinearVelocity(lua_State* L) { static int l_lovrColliderSetLinearVelocity(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - float x = luax_checkfloat(L, 2); - float y = luax_checkfloat(L, 3); - float z = luax_checkfloat(L, 4); - lovrColliderSetLinearVelocity(collider, x, y, z); + float velocity[4]; + luax_readvec3(L, 2, velocity, NULL); + lovrColliderSetLinearVelocity(collider, velocity[0], velocity[1], velocity[2]); return 0; } @@ -289,10 +281,9 @@ static int l_lovrColliderGetAngularVelocity(lua_State* L) { static int l_lovrColliderSetAngularVelocity(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - float x = luax_checkfloat(L, 2); - float y = luax_checkfloat(L, 3); - float z = luax_checkfloat(L, 4); - lovrColliderSetAngularVelocity(collider, x, y, z); + float velocity[4]; + luax_readvec3(L, 2, velocity, NULL); + lovrColliderSetAngularVelocity(collider, velocity[0], velocity[1], velocity[2]); return 0; } From e553672b5aab3cf3df125c501e3dc67d102589ce Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 14 Mar 2020 16:46:35 -0700 Subject: [PATCH 36/44] Fix sampler uniform error message enum; Surprisingly, this appears to be the only place reading out of enum arrays like this, so there shouldn't be any other places to fix. --- src/api/l_graphics_shader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/l_graphics_shader.c b/src/api/l_graphics_shader.c index ca8fd128d..e163c0cdd 100644 --- a/src/api/l_graphics_shader.c +++ b/src/api/l_graphics_shader.c @@ -65,7 +65,7 @@ int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* des case UNIFORM_SAMPLER: { *((Texture**) dest + i) = luax_checktype(L, j, Texture); TextureType type = lovrTextureGetType(*((Texture**) dest + i)); - lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s sampler uniform", TextureTypes[type], TextureTypes[uniform->textureType]); + lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s sampler uniform", TextureTypes[type].string, TextureTypes[uniform->textureType].string); break; } From 37d8df478472f3e6d62f91d673a9c3b123c80ec7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 29 Mar 2020 13:32:32 -0600 Subject: [PATCH 37/44] Allow initializing empty TextureData with Blob; --- src/api/l_data.c | 16 +++++++++++----- src/modules/data/rasterizer.c | 2 +- src/modules/data/textureData.c | 17 ++++++++++++----- src/modules/data/textureData.h | 2 +- src/modules/graphics/font.c | 2 +- src/modules/graphics/opengl.c | 4 ++-- src/modules/headset/openvr.c | 2 +- 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/api/l_data.c b/src/api/l_data.c index cb7c95163..29dd5a45b 100644 --- a/src/api/l_data.c +++ b/src/api/l_data.c @@ -119,12 +119,18 @@ static int l_lovrDataNewTextureData(lua_State* L) { int width = luaL_checkinteger(L, 1); int height = luaL_checkinteger(L, 2); TextureFormat format = luax_checkenum(L, 3, TextureFormats, "rgba", "TextureFormat"); - textureData = lovrTextureDataCreate(width, height, 0x0, format); + Blob* blob = lua_isnoneornil(L, 4) ? NULL : luax_checktype(L, 4, Blob); + textureData = lovrTextureDataCreate(width, height, blob, 0x0, format); } else { - Blob* blob = luax_readblob(L, 1, "Texture"); - bool flip = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2); - textureData = lovrTextureDataCreateFromBlob(blob, flip); - lovrRelease(Blob, blob); + TextureData* source = luax_totype(L, 1, TextureData); + if (source) { + textureData = lovrTextureDataCreate(source->width, source->height, source->blob, 0x0, source->format); + } else { + Blob* blob = luax_readblob(L, 1, "Texture"); + bool flip = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2); + textureData = lovrTextureDataCreateFromBlob(blob, flip); + lovrRelease(Blob, blob); + } } luax_pushtype(L, TextureData, textureData); diff --git a/src/modules/data/rasterizer.c b/src/modules/data/rasterizer.c index 5cc59b210..79fbcf27c 100644 --- a/src/modules/data/rasterizer.c +++ b/src/modules/data/rasterizer.c @@ -125,7 +125,7 @@ void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph* glyph->dx = empty ? 0 : roundf(bearing * rasterizer->scale); glyph->dy = empty ? 0 : roundf(y1 * rasterizer->scale); glyph->advance = roundf(advance * rasterizer->scale); - glyph->data = lovrTextureDataCreate(glyph->tw, glyph->th, 0, FORMAT_RGB); + glyph->data = lovrTextureDataCreate(glyph->tw, glyph->th, NULL, 0, FORMAT_RGB); // Render SDF float tx = GLYPH_PADDING + -glyph->dx; diff --git a/src/modules/data/textureData.c b/src/modules/data/textureData.c index bd9db9695..c3913c35a 100644 --- a/src/modules/data/textureData.c +++ b/src/modules/data/textureData.c @@ -452,17 +452,22 @@ static bool parseASTC(uint8_t* bytes, size_t size, TextureData* textureData) { return true; } -TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, uint8_t value, TextureFormat format) { - lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive"); - lovrAssert(format < FORMAT_DXT1, "Blank TextureData cannot be compressed"); +TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, Blob* contents, uint8_t value, TextureFormat format) { size_t pixelSize = getPixelSize(format); size_t size = width * height * pixelSize; + lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive"); + lovrAssert(format < FORMAT_DXT1, "Blank TextureData cannot be compressed"); + lovrAssert(!contents || contents->size >= size, "TextureData Blob is too small (%d bytes needed, got %d)", size, contents->size); textureData->width = width; textureData->height = height; textureData->format = format; - void *data = malloc(size); + void* data = malloc(size); lovrAssert(data, "Out of memory"); - memset(data, value, size); + if (contents) { + memcpy(data, contents->data, size); + } else { + memset(data, value, size); + } textureData->blob = lovrBlobCreate(data, size, "TextureData plain"); return textureData; } @@ -489,9 +494,11 @@ TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, b if (stbi_is_hdr_from_memory(blob->data, length)) { textureData->format = FORMAT_RGBA32F; textureData->blob->data = stbi_loadf_from_memory(blob->data, length, &width, &height, NULL, 4); + textureData->blob->size = 16 * width * height; } else { textureData->format = FORMAT_RGBA; textureData->blob->data = stbi_load_from_memory(blob->data, length, &width, &height, NULL, 4); + textureData->blob->size = 4 * width * height; } if (!textureData->blob->data) { diff --git a/src/modules/data/textureData.h b/src/modules/data/textureData.h index fe7b160d9..69f13c7a8 100644 --- a/src/modules/data/textureData.h +++ b/src/modules/data/textureData.h @@ -57,7 +57,7 @@ typedef struct TextureData { uint32_t mipmapCount; } TextureData; -TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, uint8_t value, TextureFormat format); +TextureData* lovrTextureDataInit(TextureData* textureData, uint32_t width, uint32_t height, Blob* contents, uint8_t value, TextureFormat format); TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, bool flip); #define lovrTextureDataCreate(...) lovrTextureDataInit(lovrAlloc(TextureData), __VA_ARGS__) #define lovrTextureDataCreateFromBlob(...) lovrTextureDataInitFromBlob(lovrAlloc(TextureData), __VA_ARGS__) diff --git a/src/modules/graphics/font.c b/src/modules/graphics/font.c index 8cf8862ee..99a5fb9cc 100644 --- a/src/modules/graphics/font.c +++ b/src/modules/graphics/font.c @@ -368,7 +368,7 @@ static void lovrFontExpandTexture(Font* font) { // Could look into using glClearTexImage when supported to make this more efficient. static void lovrFontCreateTexture(Font* font) { lovrRelease(Texture, font->texture); - TextureData* textureData = lovrTextureDataCreate(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB); + TextureData* textureData = lovrTextureDataCreate(font->atlas.width, font->atlas.height, NULL, 0x0, FORMAT_RGB); font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false, false, 0); lovrTextureSetFilter(font->texture, (TextureFilter) { .mode = FILTER_BILINEAR }); lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP }); diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index 49e66a33e..74c10502f 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -1289,7 +1289,7 @@ void lovrGpuInit(void* (*getProcAddress)(const char*)) { arr_init(&state.incoherents[i]); } - TextureData* textureData = lovrTextureDataCreate(1, 1, 0xff, FORMAT_RGBA); + TextureData* textureData = lovrTextureDataCreate(1, 1, NULL, 0xff, FORMAT_RGBA); state.defaultTexture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, false, 0); lovrTextureSetFilter(state.defaultTexture, (TextureFilter) { .mode = FILTER_NEAREST }); lovrTextureSetWrap(state.defaultTexture, (TextureWrap) { WRAP_CLAMP, WRAP_CLAMP, WRAP_CLAMP }); @@ -2014,7 +2014,7 @@ TextureData* lovrCanvasNewTextureData(Canvas* canvas, uint32_t index) { glReadBuffer(index); } - TextureData* textureData = lovrTextureDataCreate(canvas->width, canvas->height, 0x0, FORMAT_RGBA); + TextureData* textureData = lovrTextureDataCreate(canvas->width, canvas->height, NULL, 0x0, FORMAT_RGBA); glReadPixels(0, 0, canvas->width, canvas->height, GL_RGBA, GL_UNSIGNED_BYTE, textureData->blob->data); if (index != 0) { diff --git a/src/modules/headset/openvr.c b/src/modules/headset/openvr.c index b0e781a5b..de13f3ed0 100755 --- a/src/modules/headset/openvr.c +++ b/src/modules/headset/openvr.c @@ -608,7 +608,7 @@ static ModelData* openvr_newModelData(Device device) { }; RenderModel_TextureMap_t* vrTexture = state.deviceTextures[index]; - model->textures[0] = lovrTextureDataCreate(vrTexture->unWidth, vrTexture->unHeight, 0, FORMAT_RGBA); + model->textures[0] = lovrTextureDataCreate(vrTexture->unWidth, vrTexture->unHeight, NULL, 0, FORMAT_RGBA); memcpy(model->textures[0]->blob->data, vrTexture->rubTextureMapData, vrTexture->unWidth * vrTexture->unHeight * 4); model->materials[0] = (ModelMaterial) { From aed0499c5f58e16de6e757104af1f82c254c853a Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 2 Apr 2020 22:47:09 -0600 Subject: [PATCH 38/44] Add oculus_touch to action manifest; --- src/resources/actions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/actions.json b/src/resources/actions.json index 99145fb5d..b05654a2d 100644 --- a/src/resources/actions.json +++ b/src/resources/actions.json @@ -56,7 +56,8 @@ ], "default_bindings" : [ { "binding_url" : "bindings_vive.json", "controller_type" : "vive_controller" }, - { "binding_url" : "bindings_knuckles.json", "controller_type" : "knuckles" } + { "binding_url" : "bindings_knuckles.json", "controller_type" : "knuckles" }, + { "binding_url" : "bindings_touch.json", "controller_type" : "oculus_touch" } ], "localization" : [ { From 077d90bc79c537714eaccfe6df54f62199b41f0b Mon Sep 17 00:00:00 2001 From: Colby Klein Date: Thu, 2 Apr 2020 22:35:12 -0700 Subject: [PATCH 39/44] update ovr driver to newer frame submission api as mentioned on slack. there are some situations you can get into (high load in some place or other) where the newer frame submission api will behave much more consistently, and I've noticed no negative effects. besides, the other one is deprecated as best i can tell. --- src/modules/headset/oculus.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/headset/oculus.c b/src/modules/headset/oculus.c index 6bed28fde..5cc31e808 100755 --- a/src/modules/headset/oculus.c +++ b/src/modules/headset/oculus.c @@ -19,6 +19,7 @@ static struct { bool needRefreshButtons; ovrHmdDesc desc; ovrSession session; + long long frameIndex; ovrGraphicsLuid luid; float clipNear; float clipFar; @@ -58,7 +59,7 @@ static ovrTrackingState *refreshTracking(void) { // get the state head and controllers are predicted to be in at display time, // per the manual (frame timing section). - double predicted = ovr_GetPredictedDisplayTime(state.session, 0); + double predicted = ovr_GetPredictedDisplayTime(state.session, state.frameIndex); ts = ovr_GetTrackingState(state.session, predicted, true); state.needRefreshTracking = false; return &ts; @@ -144,7 +145,7 @@ static const float* oculus_getDisplayMask(uint32_t* count) { } static double oculus_getDisplayTime(void) { - return ovr_GetPredictedDisplayTime(state.session, 0); + return ovr_GetPredictedDisplayTime(state.session, state.frameIndex); } static void getEyePoses(ovrPosef poses[2], double* sensorSampleTime) { @@ -373,6 +374,9 @@ static void oculus_renderTo(void (*callback)(void*), void* userdata) { mat4_fromMat44(camera.projection[eye], projection.M); } + ovr_WaitToBeginFrame(state.session, state.frameIndex); + ovr_BeginFrame(state.session, state.frameIndex); + int curIndex; uint32_t curTexId; ovr_GetTextureSwapChainCurrentIndex(state.session, state.chain, &curIndex); @@ -403,7 +407,8 @@ static void oculus_renderTo(void (*callback)(void*), void* userdata) { } const ovrLayerHeader* layers = &ld.Header; - ovr_SubmitFrame(state.session, 0, NULL, &layers, 1); + ovr_EndFrame(state.session, state.frameIndex, NULL, &layers, 1); + ++state.frameIndex; state.needRefreshTracking = true; state.needRefreshButtons = true; From d8abfcfae3aa92837026ecb47cde2eeb04df9726 Mon Sep 17 00:00:00 2001 From: mcc Date: Sun, 5 Apr 2020 19:51:37 -0400 Subject: [PATCH 40/44] Remove mat4_transform_project, it not only duplicates mat4_transform, it is buggy --- src/core/maf.h | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/core/maf.h b/src/core/maf.h index 6601b4c6f..a4d94c5ff 100644 --- a/src/core/maf.h +++ b/src/core/maf.h @@ -417,6 +417,7 @@ MAF mat4 mat4_invert(mat4 m) { return m; } +// Calculate matrix equivalent to "apply n, then m" MAF mat4 mat4_multiply(mat4 m, mat4 n) { float m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3], m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7], @@ -603,6 +604,8 @@ MAF mat4 mat4_lookAt(mat4 m, vec3 from, vec3 to, vec3 up) { return m; } +// Apply matrix to a vec3 +// Difference from mat4_multiplyVec4: w normalize is performed, w in vec3 is ignored MAF void mat4_transform(mat4 m, vec3 v) { float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12]; float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + m[13]; @@ -614,17 +617,6 @@ MAF void mat4_transform(mat4 m, vec3 v) { v[3] = w / w; } -MAF void mat4_transform_project(mat4 m, vec3 v) { - vec3_set(v, - v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12], - v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + m[13], - v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + m[14] - ); - float w = - v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + m[15]; - vec3_set(v, v[0]/w, v[1]/w, v[2]/w); -} - MAF void mat4_transformDirection(mat4 m, vec3 v) { float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8]; float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9]; From 14e79bf8ef420755c83d0a1bf290682b79b6c8cd Mon Sep 17 00:00:00 2001 From: Colby Klein Date: Sun, 12 Apr 2020 16:02:59 -0700 Subject: [PATCH 41/44] implement haptics on oculus --- src/modules/headset/oculus.c | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/modules/headset/oculus.c b/src/modules/headset/oculus.c index 5cc31e808..893e5b06d 100755 --- a/src/modules/headset/oculus.c +++ b/src/modules/headset/oculus.c @@ -27,6 +27,10 @@ static struct { Canvas* canvas; ovrTextureSwapChain chain; ovrMirrorTexture mirror; + float hapticFrequency[2]; + float hapticStrength[2]; + float hapticDuration[2]; + double hapticLastTime; arr_t(Texture*) textures; map_t textureLookup; } state; @@ -44,6 +48,10 @@ static Texture* lookupTexture(uint32_t handle) { return state.textures.data[index]; } +static double oculus_getDisplayTime(void) { + return ovr_GetPredictedDisplayTime(state.session, state.frameIndex); +} + static ovrTrackingState *refreshTracking(void) { static ovrTrackingState ts; if (!state.needRefreshTracking) { @@ -59,7 +67,7 @@ static ovrTrackingState *refreshTracking(void) { // get the state head and controllers are predicted to be in at display time, // per the manual (frame timing section). - double predicted = ovr_GetPredictedDisplayTime(state.session, state.frameIndex); + double predicted = oculus_getDisplayTime(); ts = ovr_GetTrackingState(state.session, predicted, true); state.needRefreshTracking = false; return &ts; @@ -144,10 +152,6 @@ static const float* oculus_getDisplayMask(uint32_t* count) { return NULL; } -static double oculus_getDisplayTime(void) { - return ovr_GetPredictedDisplayTime(state.session, state.frameIndex); -} - static void getEyePoses(ovrPosef poses[2], double* sensorSampleTime) { ovrEyeRenderDesc eyeRenderDesc[2] = { ovr_GetRenderDesc(state.session, ovrEye_Left, state.desc.DefaultEyeFov[0]), @@ -308,7 +312,18 @@ static bool oculus_getAxis(Device device, DeviceAxis axis, vec3 value) { } static bool oculus_vibrate(Device device, float strength, float duration, float frequency) { - return false; // TODO + int idx = device == DEVICE_HAND_LEFT ? 0 : 1; + state.hapticStrength[idx] = strength > 0.0f ? (strength <= 1.0f ? strength : 1.0f) : 0.0f; + state.hapticDuration[idx] = duration > 0.0f ? duration : 0.0f; + float freq = frequency / 320.0f; // 1.0 = 320hz, limit on Rift CV1 touch controllers. + if (freq < 0.0f) { + freq = 0.0f; + } + if (freq > 1.0f) { + freq = 1.0f; + } + state.hapticFrequency[idx] = freq; + return true; } static ModelData* oculus_newModelData(Device device) { @@ -349,6 +364,16 @@ static void oculus_renderTo(void (*callback)(void*), void* userdata) { double sensorSampleTime; getEyePoses(EyeRenderPose, &sensorSampleTime); + float delta = (float)(state.hapticLastTime - sensorSampleTime); + state.hapticLastTime = sensorSampleTime; + for (int i = 0; i < 2; ++i) { + ovr_SetControllerVibration(state.session, ovrControllerType_LTouch + i, state.hapticFrequency[i], state.hapticStrength[i]); + state.hapticDuration[i] -= delta; + if (state.hapticDuration[i] <= 0.0f) { + state.hapticStrength[i] = 0.0f; + } + } + Camera camera = { .canvas = state.canvas }; for (int eye = 0; eye < 2; eye++) { From 809d9b7385916566eb707453aaeac42b74a65d8d Mon Sep 17 00:00:00 2001 From: Colby Klein Date: Sun, 12 Apr 2020 16:12:06 -0700 Subject: [PATCH 42/44] ignore haptics for non-hands seems a little strange that vibrating head or something would affect the right controller. --- src/modules/headset/oculus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/headset/oculus.c b/src/modules/headset/oculus.c index 893e5b06d..d398555e8 100755 --- a/src/modules/headset/oculus.c +++ b/src/modules/headset/oculus.c @@ -312,6 +312,9 @@ static bool oculus_getAxis(Device device, DeviceAxis axis, vec3 value) { } static bool oculus_vibrate(Device device, float strength, float duration, float frequency) { + if (device != DEVICE_HAND_LEFT && device != DEVICE_HAND_RIGHT) { + return false; + } int idx = device == DEVICE_HAND_LEFT ? 0 : 1; state.hapticStrength[idx] = strength > 0.0f ? (strength <= 1.0f ? strength : 1.0f) : 0.0f; state.hapticDuration[idx] = duration > 0.0f ? duration : 0.0f; From 34cef2673cd902b41dd16bc69c605cc9edc6614b Mon Sep 17 00:00:00 2001 From: Colby Klein Date: Sun, 12 Apr 2020 16:17:01 -0700 Subject: [PATCH 43/44] use clamp/max macros --- src/modules/headset/oculus.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/modules/headset/oculus.c b/src/modules/headset/oculus.c index d398555e8..bb57bb4da 100755 --- a/src/modules/headset/oculus.c +++ b/src/modules/headset/oculus.c @@ -316,15 +316,9 @@ static bool oculus_vibrate(Device device, float strength, float duration, float return false; } int idx = device == DEVICE_HAND_LEFT ? 0 : 1; - state.hapticStrength[idx] = strength > 0.0f ? (strength <= 1.0f ? strength : 1.0f) : 0.0f; - state.hapticDuration[idx] = duration > 0.0f ? duration : 0.0f; - float freq = frequency / 320.0f; // 1.0 = 320hz, limit on Rift CV1 touch controllers. - if (freq < 0.0f) { - freq = 0.0f; - } - if (freq > 1.0f) { - freq = 1.0f; - } + state.hapticStrength[idx] = CLAMP(strength, 0.0f, 1.0f); + state.hapticDuration[idx] = MAX(duration, 0.0f); + float freq = CLAMP(frequency / 320.0f, 0.0f, 1.0f); // 1.0 = 320hz, limit on Rift CV1 touch controllers. state.hapticFrequency[idx] = freq; return true; } From 0ef8d743fcd53186d2ebd32093c9f76bd21e1357 Mon Sep 17 00:00:00 2001 From: mcc Date: Wed, 15 Apr 2020 14:13:29 -0400 Subject: [PATCH 44/44] Fix bugs: activate gamepad driver properly, multiple-detect bug --- src/api/l_headset.c | 1 + src/modules/headset/gamepad.c | 1 + src/resources/boot.lua | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/l_headset.c b/src/api/l_headset.c index 04c139cb9..cd5d3f9f2 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -21,6 +21,7 @@ StringEntry HeadsetDrivers[] = { [DRIVER_OPENXR] = ENTRY("openxr"), [DRIVER_WEBVR] = ENTRY("webvr"), [DRIVER_WEBXR] = ENTRY("webxr"), + [DRIVER_GAMEPAD] = ENTRY("gamepad"), { 0 } }; diff --git a/src/modules/headset/gamepad.c b/src/modules/headset/gamepad.c index 402d83d49..4b60cbdc4 100644 --- a/src/modules/headset/gamepad.c +++ b/src/modules/headset/gamepad.c @@ -46,6 +46,7 @@ void discoverGamepads() { gamepad->jid = jid; state.gamepadsPresent++; printf("FOUND NEW GAMEPAD %d: %s\n", gamepadIdx, glfwGetJoystickName(jid)); + break; } } } diff --git a/src/resources/boot.lua b/src/resources/boot.lua index e303690e0..33c04e987 100644 --- a/src/resources/boot.lua +++ b/src/resources/boot.lua @@ -96,7 +96,7 @@ function lovr.boot() timer = true }, headset = { - drivers = { 'leap', 'openxr', 'oculus', 'oculusmobile', 'openvr', 'webxr', 'webvr', 'desktop' }, + drivers = { 'leap', 'openxr', 'oculus', 'oculusmobile', 'openvr', 'webxr', 'webvr', 'gamepad', 'desktop' }, offset = 1.7, msaa = 4 },