pre-git state
This commit is contained in:
commit
d8e4480819
18
.clang-format
Normal file
18
.clang-format
Normal file
@ -0,0 +1,18 @@
|
||||
IndentWidth: 4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveMacros: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AlwaysBreakAfterReturnType: None
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakBeforeBraces: Attach
|
||||
IndentPPDirectives: BeforeHash
|
||||
PointerAlignment: Right
|
1819
contrib/glad/glad.c
Normal file
1819
contrib/glad/glad.c
Normal file
File diff suppressed because it is too large
Load Diff
3656
contrib/glad/glad.h
Normal file
3656
contrib/glad/glad.h
Normal file
File diff suppressed because it is too large
Load Diff
311
contrib/glad/khrplatform.h
Normal file
311
contrib/glad/khrplatform.h
Normal file
@ -0,0 +1,311 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
26
contrib/xxhash/LICENSE
Normal file
26
contrib/xxhash/LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
xxHash Library
|
||||
Copyright (c) 2012-2021 Yann Collet
|
||||
All rights reserved.
|
||||
|
||||
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
43
contrib/xxhash/xxhash.c
Normal file
43
contrib/xxhash/xxhash.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2012-2021 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* xxhash.c instantiates functions defined in xxhash.h
|
||||
*/
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
|
||||
#define XXH_IMPLEMENTATION /* access definitions */
|
||||
|
||||
#include "xxhash.h"
|
6773
contrib/xxhash/xxhash.h
Normal file
6773
contrib/xxhash/xxhash.h
Normal file
File diff suppressed because it is too large
Load Diff
49
include/fio.h
Normal file
49
include/fio.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef VY_FIO_H
|
||||
#define VY_FIO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "voyage.h"
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
size_t size;
|
||||
} vy_file_buffer;
|
||||
|
||||
/* used to identify a file (XXH3 hash of the path) */
|
||||
typedef uint64_t vy_file_id;
|
||||
|
||||
typedef unsigned int vy_fio_handle;
|
||||
|
||||
typedef struct {
|
||||
unsigned int queue_size;
|
||||
unsigned int max_file_count;
|
||||
} vy_fio_config;
|
||||
|
||||
bool vyInitFIO(const vy_fio_config *config);
|
||||
|
||||
void vyShutdownFIO(void);
|
||||
|
||||
vy_file_id vyGetFileId(const char *path);
|
||||
|
||||
vy_file_id vyGetFileIdFromSpan(vy_text_span path);
|
||||
|
||||
vy_file_id vyAddFile(const char *path);
|
||||
|
||||
vy_file_id vyAddFileFromSpan(vy_text_span path);
|
||||
|
||||
const char *vyGetFilePath(vy_file_id fid);
|
||||
|
||||
vy_fio_handle vyEnqueueRead(vy_file_id fid);
|
||||
|
||||
void vyAbortFIO(vy_fio_handle fio);
|
||||
|
||||
bool vyIsFIOFinished(vy_fio_handle fio);
|
||||
|
||||
bool vyRetrieveReadBuffer(vy_fio_handle fio, vy_file_buffer *buffer);
|
||||
|
||||
void vyFreeFileBuffer(vy_file_buffer buffer);
|
||||
|
||||
#endif
|
56
include/gfx.h
Normal file
56
include/gfx.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef VY_GFX_H
|
||||
#define VY_GFX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* graphics system. this is the interface of the rendering code.
|
||||
*
|
||||
* we need (at least) three different renderers:
|
||||
* - world cell renderer (for world & dungeon environments)
|
||||
* - character renderer (for animated models)
|
||||
* - object renderer (for static models)
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool vyInitRenderer(void);
|
||||
|
||||
void vyShutdownRenderer(void);
|
||||
|
||||
/* Generational indices for backend objects */
|
||||
typedef struct {
|
||||
uint32_t index;
|
||||
} vy_gfx_pipeline_id;
|
||||
|
||||
#define VY_IS_GFX_ID_VALID(id) ((id).index != 0)
|
||||
|
||||
/* Attributes are used to bind buffers (or textures) to symbolic values.
|
||||
* For example, an attribute might be bound to "CELL_GRID", which would be
|
||||
* replaced with the (at the time of the invoke) grid buffer of the current
|
||||
* world cell.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
VY_ATTRIBUTE_VALUE_UNDEFINED,
|
||||
|
||||
VY_ATTRIBUTE_VALUE_MATERIAL_ALBEDO,
|
||||
} vy_attribute_value;
|
||||
|
||||
typedef struct {
|
||||
uint32_t index;
|
||||
vy_attribute_value value;
|
||||
} vy_attribute_binding;
|
||||
|
||||
typedef struct {
|
||||
vy_attribute_binding *uniform_bindings;
|
||||
vy_attribute_binding *storage_bindings;
|
||||
vy_attribute_binding *texture_bindings;
|
||||
|
||||
vy_gfx_pipeline_id pipeline;
|
||||
|
||||
unsigned int uniform_binding_count;
|
||||
unsigned int storage_binding_count;
|
||||
unsigned int texture_binding_count;
|
||||
} vy_shader;
|
||||
|
||||
#endif
|
29
include/gfx_backend.h
Normal file
29
include/gfx_backend.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VY_GFX_BACKEND_H
|
||||
#define VY_GFX_BACKEND_H
|
||||
|
||||
/* Backend functions and types. */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
typedef struct {
|
||||
const char *compute_source;
|
||||
size_t compute_source_length;
|
||||
} vy_compute_pipeline_info;
|
||||
|
||||
typedef struct {
|
||||
const char *vertex_source;
|
||||
size_t vertex_source_length;
|
||||
|
||||
const char *fragment_source;
|
||||
size_t fragment_source_length;
|
||||
} vy_graphics_pipeline_info;
|
||||
|
||||
vy_gfx_pipeline_id
|
||||
vyCompileComputePipeline(const vy_compute_pipeline_info *info);
|
||||
|
||||
vy_gfx_pipeline_id
|
||||
vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info);
|
||||
|
||||
#endif
|
17
include/voyage.h
Normal file
17
include/voyage.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef VY_VOYAGE_H
|
||||
#define VY_VOYAGE_H
|
||||
|
||||
/* basic types and macros */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define VY_UNUSED(x) ((void)sizeof((x)))
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
unsigned int length;
|
||||
} vy_text_span;
|
||||
|
||||
void vyReportError(const char *subsystem, const char *fmt, ...);
|
||||
|
||||
#endif
|
51
meson.build
Normal file
51
meson.build
Normal file
@ -0,0 +1,51 @@
|
||||
project('voyage', 'c',
|
||||
default_options: ['buildtype=debug', 'b_sanitize=address', 'c_std=c17', 'warning_level=3'])
|
||||
|
||||
compiler = meson.get_compiler('c')
|
||||
buildtype = get_option('buildtype')
|
||||
|
||||
# Build options
|
||||
if compiler.get_argument_syntax() == 'gcc'
|
||||
add_project_arguments(
|
||||
['-Wconversion', '-Wno-sign-conversion',
|
||||
'-Wdouble-promotion', '-Wno-unused-function', '-Wno-unused-parameter'],
|
||||
language : 'c'
|
||||
)
|
||||
elif compiler.get_argument_syntax() == 'msvc'
|
||||
add_project_arguments(
|
||||
['/wd4146', '/wd4245', '/wd4100', '/D_CRT_SECURE_NO_WARNINGS', '/RTcsu'],
|
||||
language: 'c'
|
||||
)
|
||||
endif
|
||||
|
||||
# Debug specific flags
|
||||
if buildtype == 'debug'
|
||||
add_project_arguments([ '-DDEBUG'], language : 'c')
|
||||
endif
|
||||
|
||||
# "Select" renderer backend
|
||||
# Currently only OpenGL is supported
|
||||
add_project_arguments([ '-DRENDERER_GL'], language : 'c')
|
||||
|
||||
# Gather dependencies
|
||||
thread_dep = dependency('threads')
|
||||
m_dep = compiler.find_library('m', required : false)
|
||||
glfw_proj = subproject('glfw')
|
||||
glfw_dep = glfw_proj.get_variable('glfw_dep')
|
||||
|
||||
incdir = include_directories(['contrib', 'include'])
|
||||
|
||||
executable('voyage',
|
||||
# Project Sources
|
||||
'src/voyage.c',
|
||||
'src/fio.c',
|
||||
'src/error_report.c',
|
||||
'src/gfx_main.c',
|
||||
'src/gfx_shader_loading.c',
|
||||
'src/gfx_pipelines.c',
|
||||
|
||||
# Contrib Sources
|
||||
'contrib/glad/glad.c',
|
||||
'contrib/xxhash/xxhash.c',
|
||||
dependencies : [thread_dep, m_dep, glfw_dep],
|
||||
include_directories: incdir)
|
7
shader/cell.shader
Normal file
7
shader/cell.shader
Normal file
@ -0,0 +1,7 @@
|
||||
vertex shader/cell_vert.glsl;
|
||||
fragment shader/cell_frag.glsl;
|
||||
|
||||
texture_bindings {
|
||||
0 MATERIAL_ALBEDO;
|
||||
1 MATERIAL_NORMAL;
|
||||
}
|
15
src/error_report.c
Normal file
15
src/error_report.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "voyage.h"
|
||||
|
||||
/* TODO(Kevin): Log to file, show error message box, ... */
|
||||
|
||||
void vyReportError(const char *subsystem, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "[%s] ", subsystem);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
358
src/fio.c
Normal file
358
src/fio.c
Normal file
@ -0,0 +1,358 @@
|
||||
#include "fio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xxhash/xxhash.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#define FLAGS_FINISHED 0x001
|
||||
#define FLAGS_RETRIEVED 0x002
|
||||
#define FLAGS_IN_USE 0x004
|
||||
typedef struct {
|
||||
vy_file_id fid;
|
||||
vy_file_buffer buffer;
|
||||
unsigned int flags;
|
||||
} vy_file_op;
|
||||
|
||||
/* Ringbuffer of file io operations */
|
||||
typedef struct {
|
||||
vy_file_op *ops;
|
||||
unsigned int size;
|
||||
unsigned int write_pos;
|
||||
unsigned int read_pos;
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t pending_cond;
|
||||
#endif
|
||||
} vy_fio_queue;
|
||||
|
||||
#define NAME_CAP(cap) ((cap)*128)
|
||||
typedef struct {
|
||||
vy_file_id *ids;
|
||||
unsigned int *name_offsets;
|
||||
char *names;
|
||||
unsigned int capacity;
|
||||
unsigned int name_head;
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_t mutex;
|
||||
#endif
|
||||
} vy_file_tab;
|
||||
|
||||
static vy_fio_queue _queue;
|
||||
static vy_file_tab _file_tab;
|
||||
|
||||
#ifdef __linux__
|
||||
static pthread_t _thread;
|
||||
#endif
|
||||
|
||||
static bool InitFIOQueue(unsigned int size) {
|
||||
_queue.ops = calloc(size, sizeof(vy_file_op));
|
||||
if (!_queue.ops)
|
||||
return false;
|
||||
_queue.write_pos = 0;
|
||||
_queue.read_pos = 0;
|
||||
_queue.size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ShutdownFIOQueue(void) {
|
||||
for (unsigned int i = 0; i < _queue.size; ++i) {
|
||||
free(_queue.ops[i].buffer.data);
|
||||
}
|
||||
free(_queue.ops);
|
||||
}
|
||||
|
||||
static bool InitFileTab(unsigned int max_files) {
|
||||
_file_tab.ids = calloc(max_files, sizeof(vy_file_id));
|
||||
if (!_file_tab.ids)
|
||||
return false;
|
||||
_file_tab.name_offsets = calloc(max_files, sizeof(unsigned int));
|
||||
if (!_file_tab.name_offsets)
|
||||
return false;
|
||||
_file_tab.names = malloc(NAME_CAP(max_files));
|
||||
if (!_file_tab.names)
|
||||
return false;
|
||||
|
||||
_file_tab.capacity = max_files;
|
||||
_file_tab.name_head = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
if (pthread_mutex_init(&_file_tab.mutex, NULL) != 0)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ShutdownFileTab(void) {
|
||||
free(_file_tab.ids);
|
||||
free(_file_tab.names);
|
||||
free(_file_tab.name_offsets);
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_destroy(&_file_tab.mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *FIOThreadProc(void *);
|
||||
|
||||
bool vyInitFIO(const vy_fio_config *config) {
|
||||
unsigned int queue_size = (config->queue_size) ? config->queue_size : 512;
|
||||
unsigned int max_file_count =
|
||||
(config->max_file_count) ? config->max_file_count : 512;
|
||||
|
||||
if (!InitFIOQueue(queue_size))
|
||||
return false;
|
||||
|
||||
if (!InitFileTab(max_file_count))
|
||||
return false;
|
||||
|
||||
#ifdef __linux__
|
||||
if (pthread_create(&_thread, NULL, FIOThreadProc, NULL) != 0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vyShutdownFIO(void) {
|
||||
#ifdef __linux__
|
||||
pthread_cancel(_thread);
|
||||
pthread_join(_thread, NULL);
|
||||
#endif
|
||||
ShutdownFIOQueue();
|
||||
ShutdownFileTab();
|
||||
}
|
||||
|
||||
vy_file_id vyGetFileId(const char *path) {
|
||||
vy_text_span span;
|
||||
span.start = path;
|
||||
span.length = (unsigned int)strlen(path);
|
||||
return vyGetFileIdFromSpan(span);
|
||||
}
|
||||
|
||||
vy_file_id vyGetFileIdFromSpan(vy_text_span path) {
|
||||
/* Randomly choosen, aka finger smash keyboard */
|
||||
XXH64_hash_t seed = 15340978;
|
||||
vy_file_id fid = (vy_file_id)XXH64(path.start, path.length, seed);
|
||||
if (fid == 0)
|
||||
fid = ~fid;
|
||||
return fid;
|
||||
}
|
||||
|
||||
vy_file_id vyAddFileFromSpan(vy_text_span path) {
|
||||
vy_file_id fid = vyGetFileIdFromSpan(path);
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_file_tab.mutex);
|
||||
#endif
|
||||
/* Hash Insert */
|
||||
unsigned int i = 0;
|
||||
unsigned int base = (unsigned int)(fid % _file_tab.capacity);
|
||||
while (i < _file_tab.capacity) {
|
||||
unsigned int at = (base + i) % _file_tab.capacity;
|
||||
if (_file_tab.ids[at] == 0) {
|
||||
/* Insert */
|
||||
unsigned int slen = (unsigned int)path.length + 1;
|
||||
if ((_file_tab.name_head + slen) >= NAME_CAP(_file_tab.capacity)) {
|
||||
/* Out of name storage */
|
||||
fid = 0;
|
||||
break;
|
||||
}
|
||||
memcpy(_file_tab.names + _file_tab.name_head, path.start, slen);
|
||||
_file_tab.name_offsets[at] = _file_tab.name_head;
|
||||
_file_tab.ids[at] = fid;
|
||||
_file_tab.name_head += slen;
|
||||
break;
|
||||
} else if (_file_tab.ids[at] == fid) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
/* Out of space */
|
||||
if (i == _file_tab.capacity)
|
||||
fid = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_file_tab.mutex);
|
||||
#endif
|
||||
return fid;
|
||||
}
|
||||
|
||||
vy_file_id vyAddFile(const char *path) {
|
||||
vy_text_span span;
|
||||
span.start = path;
|
||||
span.length = (unsigned int)strlen(path);
|
||||
return vyAddFileFromSpan(span);
|
||||
}
|
||||
|
||||
const char *vyGetFilePath(vy_file_id fid) {
|
||||
/* Hash Lookup */
|
||||
if (fid == 0)
|
||||
return NULL;
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_file_tab.mutex);
|
||||
#endif
|
||||
const char *result = NULL;
|
||||
unsigned int i = 0;
|
||||
unsigned int base = (unsigned int)(fid % _file_tab.capacity);
|
||||
while (i < _file_tab.capacity) {
|
||||
unsigned int at = (base + i) % _file_tab.capacity;
|
||||
if (_file_tab.ids[at] == fid) {
|
||||
result = _file_tab.names + _file_tab.name_offsets[at];
|
||||
break;
|
||||
} else if (_file_tab.ids[at] == 0) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_file_tab.mutex);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
vy_fio_handle vyEnqueueRead(vy_file_id fid) {
|
||||
vy_fio_handle handle = 0;
|
||||
|
||||
do {
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_queue.mutex);
|
||||
#endif
|
||||
|
||||
if (_queue.ops[_queue.write_pos].flags == 0 ||
|
||||
((_queue.ops[_queue.write_pos].flags & FLAGS_FINISHED) != 0 &&
|
||||
(_queue.ops[_queue.write_pos].flags & FLAGS_RETRIEVED) != 0)) {
|
||||
|
||||
_queue.ops[_queue.write_pos].fid = fid;
|
||||
_queue.ops[_queue.write_pos].flags = FLAGS_IN_USE;
|
||||
|
||||
handle = _queue.write_pos + 1;
|
||||
_queue.write_pos = (_queue.write_pos + 1) % _queue.size;
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_cond_signal(&_queue.pending_cond);
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
#endif
|
||||
} while (1);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void vyAbortFIO(vy_fio_handle fio) {
|
||||
if (fio == 0)
|
||||
return;
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_queue.mutex);
|
||||
#endif
|
||||
_queue.ops[fio - 1].flags = 0;
|
||||
if (_queue.ops[fio - 1].buffer.data) {
|
||||
free(_queue.ops[fio - 1].buffer.data);
|
||||
_queue.ops[fio - 1].buffer.size = 0;
|
||||
}
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool vyIsFIOFinished(vy_fio_handle fio) {
|
||||
if (fio == 0)
|
||||
return false;
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_queue.mutex);
|
||||
#endif
|
||||
bool result = (_queue.ops[fio - 1].flags & FLAGS_FINISHED) != 0;
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool vyRetrieveReadBuffer(vy_fio_handle fio, vy_file_buffer *buffer) {
|
||||
if (fio == 0)
|
||||
return false;
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_lock(&_queue.mutex);
|
||||
#endif
|
||||
|
||||
bool is_finished = (_queue.ops[fio - 1].flags & FLAGS_FINISHED) != 0;
|
||||
if (is_finished) {
|
||||
size_t sz = _queue.ops[fio - 1].buffer.size;
|
||||
buffer->data = malloc(sz);
|
||||
if (!buffer->data)
|
||||
return false;
|
||||
buffer->size = sz;
|
||||
memcpy(buffer->data, _queue.ops[fio - 1].buffer.data, sz);
|
||||
_queue.ops[fio - 1].flags |= FLAGS_RETRIEVED;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
#endif
|
||||
|
||||
return is_finished;
|
||||
}
|
||||
|
||||
void vyFreeFileBuffer(vy_file_buffer buffer) {
|
||||
free(buffer.data);
|
||||
}
|
||||
|
||||
static void ProcessRead(vy_file_op *op) {
|
||||
const char *path = vyGetFilePath(op->fid);
|
||||
if (!path) {
|
||||
op->flags |= FLAGS_FINISHED;
|
||||
return;
|
||||
}
|
||||
|
||||
op->buffer.data = NULL;
|
||||
op->buffer.size = 0;
|
||||
|
||||
FILE *file = fopen(path, "rb");
|
||||
if (!file) {
|
||||
op->flags |= FLAGS_FINISHED;
|
||||
return;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fsz = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
op->buffer.data = malloc(fsz);
|
||||
op->buffer.size = (size_t)fsz;
|
||||
if (fread(op->buffer.data, fsz, 1, file) != 1) {
|
||||
free(op->buffer.data);
|
||||
op->buffer.data = NULL;
|
||||
op->buffer.size = 0;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
op->flags |= FLAGS_FINISHED;
|
||||
}
|
||||
|
||||
static void *FIOThreadProc(void *_param) {
|
||||
#ifdef __linux__
|
||||
while (true) {
|
||||
pthread_mutex_lock(&_queue.mutex);
|
||||
while (_queue.write_pos == _queue.read_pos) {
|
||||
pthread_cond_wait(&_queue.pending_cond, &_queue.mutex);
|
||||
}
|
||||
ProcessRead(&_queue.ops[_queue.read_pos]);
|
||||
_queue.read_pos = (_queue.read_pos + 1) % _queue.size;
|
||||
pthread_mutex_unlock(&_queue.mutex);
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
23
src/gfx_main.c
Normal file
23
src/gfx_main.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
/* Attributes are used to bind buffers (or textures) to symbolic values.
|
||||
* For example, an attribute might be bound to "CELL_GRID", which would be
|
||||
* replaced with the (at the time of the invoke) grid buffer of the current
|
||||
* world cell.
|
||||
*/
|
||||
|
||||
bool vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count);
|
||||
|
||||
bool vyInitRenderer(void) {
|
||||
/* Init shader programs */
|
||||
const char *shader_files[] = {"shader/cell.shader"};
|
||||
vy_shader shaders[1];
|
||||
if (!vyLoadShaders(shader_files, shaders, 1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void vyShutdownRenderer(void) {
|
||||
}
|
155
src/gfx_pipelines.c
Normal file
155
src/gfx_pipelines.c
Normal file
@ -0,0 +1,155 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "gfx_backend.h"
|
||||
#include "voyage.h"
|
||||
|
||||
typedef struct {
|
||||
GLuint prog;
|
||||
} vy_gl_pipeline;
|
||||
|
||||
#define NUM_SLOTS 256
|
||||
|
||||
typedef struct {
|
||||
uint32_t generation_in_use[NUM_SLOTS];
|
||||
vy_gl_pipeline pipelines[NUM_SLOTS];
|
||||
} vy_pipeline_storage;
|
||||
|
||||
static vy_pipeline_storage _storage;
|
||||
|
||||
static vy_gfx_pipeline_id StorePipeline(vy_gl_pipeline pipeline) {
|
||||
/* Search for free slot */
|
||||
uint32_t slot = NUM_SLOTS;
|
||||
for (uint32_t i = 0; i < NUM_SLOTS; ++i) {
|
||||
if ((_storage.generation_in_use[i] & 0x1) == 0) {
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot == NUM_SLOTS) {
|
||||
vyReportError("GL_GFX", "Ran out of pipeline storage slots");
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
|
||||
uint32_t generation = _storage.generation_in_use[slot] >> 1;
|
||||
generation = (generation + 1) & 0x1;
|
||||
|
||||
_storage.pipelines[slot] = pipeline;
|
||||
_storage.generation_in_use[slot] = (generation << 1) | 0x1;
|
||||
|
||||
vy_gfx_pipeline_id id;
|
||||
id.index = (generation << 27) | slot;
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ReleasePipelineSlot(vy_gfx_pipeline_id id) {
|
||||
uint32_t slot = id.index & 0x08ffffff;
|
||||
uint32_t gen = (id.index >> 27) & 0x1f;
|
||||
if (slot >= NUM_SLOTS)
|
||||
return;
|
||||
gen = gen << 1 | 0x1;
|
||||
if (_storage.generation_in_use[slot] == gen)
|
||||
_storage.generation_in_use[slot] &= ~0x1;
|
||||
}
|
||||
|
||||
vy_gfx_pipeline_id
|
||||
vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
|
||||
char info_log[512];
|
||||
|
||||
GLuint prog = glCreateProgram();
|
||||
GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
|
||||
|
||||
GLchar *code = (GLchar *)info->compute_source;
|
||||
GLint code_len = (GLint)info->compute_source_length;
|
||||
glShaderSource(shader, 1, (const GLchar **)&code, &code_len);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(shader, 512, NULL, info_log);
|
||||
vyReportError("GL_GFX",
|
||||
"Failed to compile compute shader\n%s",
|
||||
info_log);
|
||||
glDeleteProgram(prog);
|
||||
glDeleteShader(shader);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
glAttachShader(prog, shader);
|
||||
glLinkProgram(prog);
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetProgramInfoLog(prog, 512, NULL, info_log);
|
||||
vyReportError("GL_GFX", "Failed to link compute shader\n%s", info_log);
|
||||
glDeleteShader(shader);
|
||||
glDeleteProgram(prog);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
glDeleteShader(shader);
|
||||
|
||||
vy_gl_pipeline pipeline;
|
||||
pipeline.prog = prog;
|
||||
return StorePipeline(pipeline);
|
||||
}
|
||||
|
||||
vy_gfx_pipeline_id
|
||||
vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
|
||||
char info_log[512];
|
||||
|
||||
GLuint prog = glCreateProgram();
|
||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
GLchar *code = (GLchar *)info->vertex_source;
|
||||
GLint code_len = (GLint)info->vertex_source_length;
|
||||
glShaderSource(vertex_shader, 1, (const GLchar **)&code, &code_len);
|
||||
glCompileShader(vertex_shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
|
||||
vyReportError("GL_GFX",
|
||||
"Failed to compile vertex shader\n%s",
|
||||
info_log);
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
glDeleteProgram(prog);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
glAttachShader(prog, vertex_shader);
|
||||
|
||||
code = (GLchar *)info->fragment_source;
|
||||
code_len = (GLint)info->fragment_source_length;
|
||||
glShaderSource(fragment_shader, 1, (const GLchar **)&code, &code_len);
|
||||
glCompileShader(fragment_shader);
|
||||
|
||||
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
|
||||
vyReportError("GFX", "Failed to compile fragment shader\n%s", info_log);
|
||||
glDeleteShader(fragment_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
glDeleteProgram(prog);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
glAttachShader(prog, fragment_shader);
|
||||
|
||||
glLinkProgram(prog);
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetProgramInfoLog(prog, 512, NULL, info_log);
|
||||
vyReportError("GFX", "Failed to link graphics shader\n%s", info_log);
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
glDeleteProgram(prog);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
|
||||
vy_gl_pipeline pipeline;
|
||||
pipeline.prog = prog;
|
||||
return StorePipeline(pipeline);
|
||||
}
|
563
src/gfx_shader_loading.c
Normal file
563
src/gfx_shader_loading.c
Normal file
@ -0,0 +1,563 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fio.h"
|
||||
#include "gfx.h"
|
||||
#include "gfx_backend.h"
|
||||
#include "voyage.h"
|
||||
|
||||
typedef enum {
|
||||
VY_STMT_FORM_VALUE,
|
||||
VY_STMT_FORM_LIST,
|
||||
} vy_stmt_form;
|
||||
|
||||
typedef struct {
|
||||
unsigned int first;
|
||||
unsigned int count;
|
||||
} vy_parsed_stmt_list;
|
||||
|
||||
typedef struct {
|
||||
vy_stmt_form form;
|
||||
vy_text_span attribute;
|
||||
union {
|
||||
vy_text_span value;
|
||||
unsigned int list_index;
|
||||
};
|
||||
/* For lists */
|
||||
unsigned int next;
|
||||
} vy_parsed_stmt;
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
const char *text;
|
||||
size_t at;
|
||||
size_t length;
|
||||
int line;
|
||||
|
||||
vy_parsed_stmt *statements;
|
||||
unsigned int statement_count;
|
||||
unsigned int statement_capacity;
|
||||
|
||||
vy_parsed_stmt_list *statement_lists;
|
||||
unsigned int statement_list_count;
|
||||
unsigned int statement_list_capacity;
|
||||
} vy_parse_state;
|
||||
|
||||
static bool IsAllowedChar(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') || (c == '.') || (c == '_') || (c == '/');
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n';
|
||||
}
|
||||
|
||||
static void SkipWhitespace(vy_parse_state *state) {
|
||||
while (state->at < state->length && IsWhitespace(state->text[state->at])) {
|
||||
if (state->text[state->at] == '\n')
|
||||
++state->line;
|
||||
++state->at;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParseAttribute(vy_parse_state *state, vy_text_span *_name) {
|
||||
vy_text_span name;
|
||||
name.start = &state->text[state->at];
|
||||
name.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at])) {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++name.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*_name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseValue(vy_parse_state *state, vy_text_span *_value) {
|
||||
vy_text_span value;
|
||||
value.start = &state->text[state->at];
|
||||
value.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at]) &&
|
||||
state->text[state->at] != ';') {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++value.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*_value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index);
|
||||
|
||||
static bool ParseStmt(vy_parse_state *state, unsigned int *stmt_index) {
|
||||
vy_parsed_stmt stmt;
|
||||
stmt.next = UINT_MAX; /* end of list */
|
||||
|
||||
SkipWhitespace(state);
|
||||
if (!ParseAttribute(state, &stmt.attribute))
|
||||
return false;
|
||||
SkipWhitespace(state);
|
||||
|
||||
if (state->at == state->length) {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Expected either a value or '{'",
|
||||
state->file,
|
||||
state->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state->text[state->at] == '{') {
|
||||
/* Consume '{' */
|
||||
++state->at;
|
||||
|
||||
stmt.form = VY_STMT_FORM_LIST;
|
||||
if (!ParseStmtList(state, &stmt.list_index))
|
||||
return false;
|
||||
|
||||
/* Consume '}' */
|
||||
if (state->at < state->length && state->text[state->at] == '}')
|
||||
++state->at;
|
||||
} else {
|
||||
stmt.form = VY_STMT_FORM_VALUE;
|
||||
if (!ParseValue(state, &stmt.value))
|
||||
return false;
|
||||
|
||||
/* Consume ';' */
|
||||
if (state->at < state->length && state->text[state->at] == ';')
|
||||
++state->at;
|
||||
}
|
||||
|
||||
SkipWhitespace(state);
|
||||
|
||||
/* Add statement to array */
|
||||
if (state->statement_count == state->statement_capacity) {
|
||||
unsigned int cap = (state->statement_capacity > 0)
|
||||
? state->statement_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt *temp =
|
||||
realloc(state->statements, sizeof(vy_parsed_stmt) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statements = temp;
|
||||
state->statement_capacity = cap;
|
||||
}
|
||||
state->statements[state->statement_count] = stmt;
|
||||
*stmt_index = state->statement_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index) {
|
||||
vy_parsed_stmt_list list;
|
||||
list.first = state->statement_count;
|
||||
list.count = 0;
|
||||
|
||||
unsigned int last = UINT_MAX;
|
||||
|
||||
while (state->at < state->length && state->text[state->at] != '}') {
|
||||
unsigned int stmt;
|
||||
if (!ParseStmt(state, &stmt))
|
||||
return false;
|
||||
|
||||
if (last != UINT_MAX)
|
||||
state->statements[last].next = stmt;
|
||||
last = stmt;
|
||||
|
||||
++list.count;
|
||||
}
|
||||
|
||||
/* Add list to array */
|
||||
if (state->statement_list_count == state->statement_list_capacity) {
|
||||
unsigned int cap = (state->statement_list_capacity > 0)
|
||||
? state->statement_list_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt_list *temp =
|
||||
realloc(state->statement_lists, sizeof(vy_parsed_stmt_list) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statement_lists = temp;
|
||||
state->statement_list_capacity = cap;
|
||||
}
|
||||
state->statement_lists[state->statement_list_count] = list;
|
||||
*list_index = state->statement_list_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DbgPrintShaderFile(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
unsigned int indent) {
|
||||
assert(list_index < state->statement_list_count);
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
for (unsigned int j = 0; j < indent; ++j)
|
||||
printf(" ");
|
||||
printf("%.*s: ", stmt->attribute.length, stmt->attribute.start);
|
||||
if (stmt->form == VY_STMT_FORM_VALUE) {
|
||||
printf("%.*s\n", stmt->value.length, stmt->value.start);
|
||||
} else {
|
||||
printf("{\n");
|
||||
DbgPrintShaderFile(state, stmt->list_index, indent + 2);
|
||||
printf("}\n");
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
assert(stmt_index = UINT_MAX);
|
||||
}
|
||||
|
||||
static bool CompareSpanToString(vy_text_span span, const char *cmp) {
|
||||
size_t cmp_len = strlen(cmp);
|
||||
if (cmp_len != (size_t)span.length)
|
||||
return false;
|
||||
for (size_t i = 0; i < cmp_len; ++i) {
|
||||
if (span.start[i] != cmp[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const vy_parsed_stmt *FindStatement(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
const char *attribute) {
|
||||
if (list_index >= state->statement_list_count)
|
||||
return NULL;
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
if (CompareSpanToString(stmt->attribute, attribute))
|
||||
return stmt;
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static vy_fio_handle DispatchFileRead(vy_text_span path) {
|
||||
vy_file_id fid = vyAddFileFromSpan(path);
|
||||
if (fid == 0)
|
||||
return 0;
|
||||
vy_fio_handle fio = vyEnqueueRead(fid);
|
||||
return fio;
|
||||
}
|
||||
|
||||
static vy_fio_handle DispatchShaderRead(const char *shader,
|
||||
const vy_parse_state *state,
|
||||
unsigned int root_list,
|
||||
const char *file_path) {
|
||||
const vy_parsed_stmt *path = FindStatement(state, root_list, shader);
|
||||
if (!path) {
|
||||
return 0;
|
||||
}
|
||||
if (path->form != VY_STMT_FORM_VALUE) {
|
||||
vyReportError("GFX",
|
||||
"Expected simple value for attribute \"%s\" in %s\n",
|
||||
shader,
|
||||
file_path);
|
||||
return 0;
|
||||
}
|
||||
return DispatchFileRead(path->value);
|
||||
}
|
||||
|
||||
static vy_gfx_pipeline_id LinkProgram(vy_parse_state *state,
|
||||
const char *file_path,
|
||||
unsigned int root_list) {
|
||||
/* Process the data */
|
||||
vy_fio_handle vertex_read =
|
||||
DispatchShaderRead("vertex", state, root_list, file_path);
|
||||
vy_fio_handle fragment_read =
|
||||
DispatchShaderRead("fragment", state, root_list, file_path);
|
||||
vy_fio_handle compute_read =
|
||||
DispatchShaderRead("compute", state, root_list, file_path);
|
||||
|
||||
if (compute_read) {
|
||||
if (vertex_read || fragment_read) {
|
||||
vyReportError("GFX",
|
||||
"A shader program can not contain a compute "
|
||||
"shader and graphics shaders: %s",
|
||||
file_path);
|
||||
vyAbortFIO(vertex_read);
|
||||
vyAbortFIO(fragment_read);
|
||||
vyAbortFIO(compute_read);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
while (!vyIsFIOFinished(compute_read)) {
|
||||
/* wait */
|
||||
}
|
||||
vy_file_buffer compute_code;
|
||||
if (!vyRetrieveReadBuffer(compute_read, &compute_code)) {
|
||||
vyReportError("GFX",
|
||||
"Failed to load compute shader required by: %s",
|
||||
file_path);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
vy_compute_pipeline_info info;
|
||||
info.compute_source = compute_code.data;
|
||||
info.compute_source_length = compute_code.size;
|
||||
vy_gfx_pipeline_id pipeline = vyCompileComputePipeline(&info);
|
||||
vyFreeFileBuffer(compute_code);
|
||||
return pipeline;
|
||||
} else if (vertex_read || fragment_read) {
|
||||
if (compute_read) {
|
||||
vyReportError("GFX",
|
||||
"A shader program can not contain a compute "
|
||||
"shader and graphics shaders: %s",
|
||||
file_path);
|
||||
vyAbortFIO(vertex_read);
|
||||
vyAbortFIO(fragment_read);
|
||||
vyAbortFIO(compute_read);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
if (!vertex_read || !fragment_read) {
|
||||
vyReportError("GFX",
|
||||
"A shader program must contain at least a vertex and "
|
||||
"a fragment shader: %s",
|
||||
file_path);
|
||||
vyAbortFIO(vertex_read);
|
||||
vyAbortFIO(fragment_read);
|
||||
vyAbortFIO(compute_read);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
|
||||
vy_graphics_pipeline_info info;
|
||||
vy_file_buffer vertex_code = {NULL, 0};
|
||||
vy_file_buffer fragment_code = {NULL, 0};
|
||||
|
||||
int remaining = 2;
|
||||
while (remaining > 0) {
|
||||
if (vyIsFIOFinished(vertex_read)) {
|
||||
if (!vyRetrieveReadBuffer(vertex_read, &vertex_code)) {
|
||||
vyReportError(
|
||||
"GFX",
|
||||
"Failed to load vertex shader required by: %s",
|
||||
file_path);
|
||||
vyFreeFileBuffer(fragment_code);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
info.vertex_source = vertex_code.data;
|
||||
info.vertex_source_length = vertex_code.size;
|
||||
--remaining;
|
||||
vertex_read = 0;
|
||||
}
|
||||
|
||||
if (vyIsFIOFinished(fragment_read)) {
|
||||
if (!vyRetrieveReadBuffer(fragment_read, &fragment_code)) {
|
||||
vyReportError(
|
||||
"GFX",
|
||||
"Failed to load fragment shader required by: %s",
|
||||
file_path);
|
||||
vyFreeFileBuffer(vertex_code);
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
info.fragment_source = fragment_code.data;
|
||||
info.fragment_source_length = fragment_code.size;
|
||||
--remaining;
|
||||
fragment_read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vy_gfx_pipeline_id pipeline = vyCompileGraphicsPipeline(&info);
|
||||
vyFreeFileBuffer(vertex_code);
|
||||
vyFreeFileBuffer(fragment_code);
|
||||
return pipeline;
|
||||
} else if (!compute_read && !vertex_read && !fragment_read) {
|
||||
vyReportError("GFX",
|
||||
"Either \"compute\" or \"vertex\" and \"fragment\" "
|
||||
"are required in %s",
|
||||
file_path);
|
||||
vyAbortFIO(vertex_read);
|
||||
vyAbortFIO(fragment_read);
|
||||
vyAbortFIO(compute_read);
|
||||
}
|
||||
return (vy_gfx_pipeline_id){0};
|
||||
}
|
||||
|
||||
static bool ParseBindingIndex(vy_text_span span, unsigned int *index) {
|
||||
if (span.length == 0)
|
||||
return false;
|
||||
int at = (int)span.length - 1;
|
||||
unsigned int exp = 1;
|
||||
unsigned int n = 0;
|
||||
while (at >= 0) {
|
||||
if (span.start[at] >= '0' && span.start[at] <= '9') {
|
||||
unsigned int digit = (unsigned int)(span.start[at] - '0');
|
||||
n += digit * exp;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"Unexpected non-digit character in binding index");
|
||||
return false;
|
||||
}
|
||||
--at;
|
||||
exp *= 10;
|
||||
}
|
||||
*index = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static vy_attribute_value ParseBindingValue(vy_text_span span) {
|
||||
if (CompareSpanToString(span, "MATERIAL_ALBEDO")) {
|
||||
return VY_ATTRIBUTE_VALUE_MATERIAL_ALBEDO;
|
||||
}
|
||||
vyReportError("GFX",
|
||||
"Unsupported binding value %*.s",
|
||||
span.length,
|
||||
span.start);
|
||||
return VY_ATTRIBUTE_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseShaderFile(vy_file_id fid, vy_file_buffer fbuf, vy_shader *shader) {
|
||||
/* This is the grammar for shader files:
|
||||
* <stmt-list> ::= <stmt>*
|
||||
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
|
||||
* <attribute> ::= [:alnum:]*
|
||||
* <value>:: = [:alnum:]* */
|
||||
const char *file_path = vyGetFilePath(fid);
|
||||
vy_parse_state state = {.text = (const char *)fbuf.data,
|
||||
.at = 0,
|
||||
.length = fbuf.size,
|
||||
.line = 1,
|
||||
.file = file_path,
|
||||
.statements = NULL,
|
||||
.statement_lists = NULL,
|
||||
.statement_capacity = 0,
|
||||
.statement_list_capacity = 0};
|
||||
|
||||
bool result = true;
|
||||
unsigned int root_list = 0;
|
||||
if (!ParseStmtList(&state, &root_list)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DbgPrintShaderFile(&state, root_list, 0);
|
||||
|
||||
shader->pipeline = LinkProgram(&state, file_path, root_list);
|
||||
if (!VY_IS_GFX_ID_VALID(shader->pipeline)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Process bindings */
|
||||
shader->texture_bindings = NULL;
|
||||
shader->texture_binding_count = 0;
|
||||
shader->uniform_bindings = NULL;
|
||||
shader->uniform_binding_count = 0;
|
||||
shader->storage_bindings = NULL;
|
||||
shader->storage_binding_count = 0;
|
||||
|
||||
const vy_parsed_stmt *texture_bindings =
|
||||
FindStatement(&state, root_list, "texture_bindings");
|
||||
if (texture_bindings) {
|
||||
if (texture_bindings->form != VY_STMT_FORM_LIST) {
|
||||
vyReportError("GFX",
|
||||
"Expected list of bindings as the value of "
|
||||
"\"texture_bindings\" in %s",
|
||||
file_path);
|
||||
goto out;
|
||||
}
|
||||
const vy_parsed_stmt_list *binding_list =
|
||||
&state.statement_lists[texture_bindings->list_index];
|
||||
shader->texture_bindings =
|
||||
malloc(sizeof(vy_attribute_binding) * binding_list->count);
|
||||
if (!shader->texture_bindings) {
|
||||
vyReportError("GFX", "Out of memory");
|
||||
goto out;
|
||||
}
|
||||
shader->texture_binding_count = binding_list->count;
|
||||
|
||||
unsigned int stmt_index = binding_list->first;
|
||||
for (unsigned int i = 0; i < binding_list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state.statements[stmt_index];
|
||||
if (!ParseBindingIndex(stmt->attribute,
|
||||
&shader->texture_bindings[i].index)) {
|
||||
free(shader->texture_bindings);
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
shader->texture_bindings[i].value = ParseBindingValue(stmt->value);
|
||||
if (shader->texture_bindings[i].value ==
|
||||
VY_ATTRIBUTE_VALUE_UNDEFINED) {
|
||||
free(shader->texture_bindings);
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
}
|
||||
|
||||
const vy_parsed_stmt *uniform_bindings =
|
||||
FindStatement(&state, root_list, "uniform_bindings");
|
||||
|
||||
const vy_parsed_stmt *storage_bindings =
|
||||
FindStatement(&state, root_list, "storage_bindings");
|
||||
|
||||
out:
|
||||
free(state.statements);
|
||||
free(state.statement_lists);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count) {
|
||||
vy_fio_handle fios[64];
|
||||
vy_file_id fids[64];
|
||||
for (unsigned int i = 0; i < count; i += 64) {
|
||||
unsigned int chunk_size = count - i;
|
||||
if (chunk_size > 64)
|
||||
chunk_size = 64;
|
||||
for (unsigned int j = 0; j < chunk_size; ++j) {
|
||||
vy_file_id fid = vyAddFile(paths[i + j]);
|
||||
if (!fid)
|
||||
return false;
|
||||
fids[j] = fid;
|
||||
fios[j] = vyEnqueueRead(fid);
|
||||
}
|
||||
|
||||
unsigned int remaining = chunk_size;
|
||||
while (remaining > 0) {
|
||||
for (unsigned int j = 0; j < chunk_size; ++j) {
|
||||
if (vyIsFIOFinished(fios[j])) {
|
||||
vy_file_buffer fbuf;
|
||||
if (!vyRetrieveReadBuffer(fios[j], &fbuf)) {
|
||||
return false;
|
||||
}
|
||||
ParseShaderFile(fids[j], fbuf, &shaders[i + j]);
|
||||
vyFreeFileBuffer(fbuf);
|
||||
--remaining;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
114
src/voyage.c
Normal file
114
src/voyage.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fio.h"
|
||||
#include "gfx.h"
|
||||
|
||||
static void glfw_error_cb(int err, const char *description) {
|
||||
fprintf(stderr, "[GLFW] %u: %s\n", err, description);
|
||||
}
|
||||
|
||||
static void make_window_windowed_fullscreen(GLFWwindow *window,
|
||||
GLFWmonitor *monitor) {
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
||||
glfwSetWindowMonitor(window,
|
||||
monitor,
|
||||
0,
|
||||
0,
|
||||
mode->width,
|
||||
mode->height,
|
||||
mode->refreshRate);
|
||||
}
|
||||
|
||||
static void make_window_windowed(GLFWwindow *window, int w, int h) {
|
||||
glfwSetWindowMonitor(window, NULL, 0, 0, w, h, GLFW_DONT_CARE);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int f1;
|
||||
int esc;
|
||||
} key_states;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
glfwSetErrorCallback(glfw_error_cb);
|
||||
if (!glfwInit()) {
|
||||
return 1;
|
||||
}
|
||||
printf("[GFLW] Version: %s\n", glfwGetVersionString());
|
||||
|
||||
/* Create a windowed full screen window */
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
|
||||
GLFWwindow *window = glfwCreateWindow(640, 480, "Voyage", NULL, NULL);
|
||||
if (!window) {
|
||||
fprintf(stderr, "[GFX] Window creation failed.\n");
|
||||
return 1;
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
if (!gladLoadGL()) {
|
||||
fprintf(stderr, "[GFX] OpenGL load failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
vy_fio_config fio_config = {0};
|
||||
if (!vyInitFIO(&fio_config)) {
|
||||
fprintf(stderr, "[FIO] Init failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!vyInitRenderer()) {
|
||||
fprintf(stderr, "[GFX] Init failed.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %u\n",
|
||||
GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
|
||||
printf("GL_MAX_UNIFORM_BUFFER_BINDINGS: %u\n",
|
||||
GL_MAX_UNIFORM_BUFFER_BINDINGS);
|
||||
|
||||
key_states prev_keys = {0};
|
||||
int window_state = 0;
|
||||
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
/* Gather input */
|
||||
key_states keys;
|
||||
keys.f1 = glfwGetKey(window, GLFW_KEY_F1);
|
||||
keys.esc = glfwGetKey(window, GLFW_KEY_ESCAPE);
|
||||
|
||||
if (keys.f1 && !prev_keys.f1) {
|
||||
if (window_state)
|
||||
make_window_windowed(window, 640, 480);
|
||||
else
|
||||
make_window_windowed_fullscreen(window,
|
||||
glfwGetPrimaryMonitor());
|
||||
window_state = !window_state;
|
||||
}
|
||||
|
||||
if (keys.esc)
|
||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
||||
|
||||
int fbw, fbh;
|
||||
glfwGetFramebufferSize(window, &fbw, &fbh);
|
||||
glViewport(0, 0, fbw, fbh);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
prev_keys = keys;
|
||||
}
|
||||
|
||||
vyShutdownRenderer();
|
||||
vyShutdownFIO();
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
12
subprojects/glfw.wrap
Normal file
12
subprojects/glfw.wrap
Normal file
@ -0,0 +1,12 @@
|
||||
[wrap-file]
|
||||
directory = glfw-3.3.8
|
||||
source_url = https://github.com/glfw/glfw/archive/refs/tags/3.3.8.tar.gz
|
||||
source_filename = glfw-3.3.8.tar.gz
|
||||
source_hash = f30f42e05f11e5fc62483e513b0488d5bceeab7d9c5da0ffe2252ad81816c713
|
||||
patch_filename = glfw_3.3.8-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/glfw_3.3.8-2/get_patch
|
||||
patch_hash = eca865a15ff49d29d1a710fda3cfb9ca82057dda7f15ed617a1677cc5992c503
|
||||
wrapdb_version = 3.3.8-2
|
||||
|
||||
[provide]
|
||||
glfw3 = glfw_dep
|
Loading…
Reference in New Issue
Block a user