Merge branch 'dev' into MSFT_controller_model

This commit is contained in:
Bjorn 2022-12-03 19:34:49 -08:00 committed by GitHub
commit 78a70670bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1007 additions and 460 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: bjornbytes

114
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,114 @@
name: Build
on: [push, pull_request]
env:
CMAKE_BUILD_TYPE: ${{ github.event_name == 'pull_request' && 'Debug' || 'Release' }}
jobs:
windows:
name: Windows
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Configure
run: cmake -B build
shell: cmd
- name: Build
run: cmake --build build --config %CMAKE_BUILD_TYPE%
shell: cmd
- name: Upload
uses: actions/upload-artifact@v3
with:
name: lovr.exe
path: |
build/Release/lovr.exe
build/Release/*.dll
linux:
name: Linux
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: sudo apt update
- name: Install Packages
run: sudo apt install -y xorg-dev libxcb-glx0-dev libfuse2
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Init
run: cmake -B build -D LOVR_BUILD_BUNDLE=ON
- name: Build
run: cmake --build build
- name: AppImage
run: >
curl -OL https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage &&
chmod +x ./appimagetool-x86_64.AppImage &&
./appimagetool-x86_64.AppImage build/bin &&
mv LÖVR-x86_64.AppImage lovr-x86_64.AppImage
- name: Upload
uses: actions/upload-artifact@v3
with:
name: lovr.appimage
path: lovr-x86_64.AppImage
android:
name: Android
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: sudo apt update
- name: Install Packages
run: sudo apt install -y glslang-tools
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Init
run: >
mkdir build &&
cd build &&
keytool
-genkey
-dname 'cn=Unknown, ou=Unknown, o=Unknown, l=Unknown, st=Unknown, c=Unknown'
-keystore key.keystore
-keypass hunter2
-storepass hunter2
-alias key
-keyalg RSA
-keysize 2048
-validity 10000 &&
cmake ..
-D CMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake
-D ANDROID_SDK=$ANDROID_HOME
-D ANDROID_ABI=arm64-v8a
-D ANDROID_NATIVE_API_LEVEL=29
-D ANDROID_BUILD_TOOLS_VERSION=30.0.3
-D ANDROID_KEYSTORE=key.keystore
-D ANDROID_KEYSTORE_PASS=pass:hunter2
- name: Build
run: cmake --build build
- name: Upload
uses: actions/upload-artifact@v3
with:
name: lovr.apk
path: build/lovr.apk
macos:
name: macOS
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Init
run: cmake -B build -D LOVR_BUILD_BUNDLE=ON
- name: Build
run: cmake --build build
- name: Upload
uses: actions/upload-artifact@v3
with:
name: lovr.app
path: build/lovr.app

3
.gitmodules vendored
View File

@ -22,3 +22,6 @@
[submodule "deps/glslang"]
path = deps/glslang
url = https://github.com/bjornbytes/glslang
[submodule "deps/vulkan-headers"]
path = deps/vulkan-headers
url = https://github.com/KhronosGroup/Vulkan-Headers

View File

@ -192,8 +192,7 @@ endif()
# Vulkan
if(LOVR_USE_VULKAN)
find_package(Vulkan REQUIRED)
include_directories(${Vulkan_INCLUDE_DIRS})
include_directories(deps/vulkan-headers/include)
endif()
# OpenXR
@ -440,7 +439,6 @@ endif()
if(LOVR_ENABLE_FILESYSTEM)
target_sources(lovr PRIVATE
src/modules/filesystem/filesystem.c
src/modules/filesystem/file.c
src/api/l_filesystem.c
)
else()
@ -475,12 +473,9 @@ if(LOVR_ENABLE_GRAPHICS)
if(LOVR_USE_WEBGPU)
target_compile_definitions(lovr PRIVATE LOVR_WGPU)
target_sources(lovr PRIVATE src/core/gpu_webgpu.c)
target_sources(lovr PRIVATE src/core/gpu_wgpu.c)
endif()
add_custom_target(compile_shaders ALL)
add_dependencies(lovr compile_shaders)
function(compile_shaders)
if(LOVR_USE_GLSLANG AND ENABLE_GLSLANG_BINARIES AND NOT ANDROID)
set(GLSLANG_VALIDATOR $<TARGET_FILE:glslangValidator>)
@ -496,15 +491,18 @@ if(LOVR_ENABLE_GRAPHICS)
foreach(shader_file ${shader_files})
string(REGEX MATCH "([^\/]+)\.${ARGV0}" shader ${shader_file})
string(REPLACE ".${ARGV0}" "" shader ${shader})
add_custom_command(TARGET compile_shaders POST_BUILD
add_custom_command(
OUTPUT ${shader_file}.h
DEPENDS ${shader_file}
COMMAND
${GLSLANG_VALIDATOR}
--quiet
--target-env vulkan1.1
--vn lovr_shader_${shader}_${ARGV0}
-o ${shader_file}.h
${shader_file}
${GLSLANG_VALIDATOR}
--quiet
--target-env vulkan1.1
--vn lovr_shader_${shader}_${ARGV0}
-o ${shader_file}.h
${shader_file}
)
target_sources(lovr PRIVATE ${shader_file}.h)
endforeach()
endfunction()

View File

@ -6,9 +6,9 @@
You can use LÖVR to easily create VR experiences without much setup or programming experience. The framework is tiny, fast, open source, and supports lots of different platforms and devices.
[![Build status](https://ci.appveyor.com/api/projects/status/alx3kdi35bmxka8c/branch/master?svg=true)](https://ci.appveyor.com/project/bjornbytes/lovr/branch/master)
[![Build](https://github.com/bjornbytes/lovr/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/bjornbytes/lovr/actions/workflows/build.yml)
[![Version](https://img.shields.io/github/release/bjornbytes/lovr.svg?label=version)](https://github.com/bjornbytes/lovr/releases)
[![Slack](https://img.shields.io/badge/chat-slack-7e4e76.svg)](https://lovr.org/slack)
[![Matrix](https://img.shields.io/badge/chat-matrix-7e4e76.svg)](https://lovr.org/matrix)
[**Homepage**](https://lovr.org) | [**Documentation**](https://lovr.org/docs) | [**FAQ**](https://lovr.org/docs/FAQ)
@ -35,7 +35,7 @@ Getting Started
---
It's really easy to get started making things with LÖVR. Grab a copy of the executable from <https://lovr.org/download>,
then write a `main.lua` script and drag its parent folder onto the executable. Here are some example projects to try:
then write a `main.lua` script and drag it onto the executable. Here are some example projects to try:
#### Hello World

View File

@ -141,6 +141,8 @@ if target == 'linux' then
end
if target == 'wasm' then
cc = 'emcc'
cxx = 'em++'
cflags += '-std=gnu11'
cflags += '-D_POSIX_C_SOURCE=200809L'
lflags += '-s FORCE_FILESYSTEM'
@ -412,9 +414,9 @@ end
for renderer, enabled in pairs(config.renderers) do
if enabled then
local code = renderer:gsub('vulkan', 'vk')
cflags += '-DLOVR_' .. code:upper()
src += 'src/core/gpu_' .. code .. '.c'
local shortname = ({ vulkan = 'vk', webgpu = 'wgpu' })[renderer]
cflags += '-DLOVR_' .. shortname:upper()
src += 'src/core/gpu_' .. shortname .. '.c'
end
end

1
deps/vulkan-headers vendored Submodule

@ -0,0 +1 @@
Subproject commit 29c0457cc167bfc9e9361a3818440e388986f5b5

View File

@ -1,55 +0,0 @@
skip_branch_with_pr: true
clone_depth: 1
only_commits:
files:
- etc/
- src/
- deps/
- CMakeLists.txt
branches:
only:
- master
- dev
environment:
VULKAN_SDK: C:/VulkanSDK
cache:
- VulkanSDK.exe
install:
- if not exist VulkanSDK.exe curl -L --silent --show-error --output VulkanSDK.exe https://sdk.lunarg.com/sdk/download/1.3.216.0/windows/VulkanSDK-1.3.216.0-Installer.exe?Human=true
- VulkanSDK.exe --root C:\VulkanSDK --accept-licenses --default-answer --confirm-command install
image: Visual Studio 2022
before_build:
- cd C:\projects\lovr
- git submodule update --init
- md build
- cd build
- cmake -DCMAKE_BUILD_TYPE=%configuration% -A x64 ..
configuration: Release
build:
project: build\lovr.sln
verbosity: quiet
after_build:
- cd C:\projects\lovr
- 7z a lovr.zip C:\projects\lovr\build\Release\*.dll C:\projects\lovr\build\Release\lovr.exe
artifacts:
- path: lovr.zip
deploy:
provider: Webhook
url: https://lovr.org/nightly
on:
branch: master
authorization:
secure: zbxwiHmtJRVP9FdiqxepLdLGjxhtpjVytb+yLSqrz+8Bl1bH6k5Zts809bLrEjzn

View File

@ -90,7 +90,7 @@ function lovr.boot()
lovr.graphics.initialize()
end
if lovr.headset and lovr.graphics and conf.window then
if lovr.headset then
lovr.headset.start()
if lovr.headset.getDriver() == 'desktop' then

View File

@ -396,7 +396,7 @@ uint32_t _luax_checku32(lua_State* L, int index) {
double x = lua_tonumber(L, index);
if (x == 0. && !lua_isnumber(L, index)) {
luaL_typerror(L, index, "number");
luax_typeerror(L, index, "number");
}
if (x < 0. || x > UINT32_MAX) {

View File

@ -2,12 +2,11 @@
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <lua.h>
#include <lauxlib.h>
#pragma once
struct lua_State;
struct luaL_Reg;
// Enums
typedef struct {
uint8_t length;
@ -107,43 +106,43 @@ typedef struct {
#define luax_optfloat(L, i, x) (float) luaL_optnumber(L, i, x)
#define luax_tofloat(L, i) (float) lua_tonumber(L, i)
void luax_preload(struct lua_State* L);
void _luax_registertype(struct lua_State* L, const char* name, const struct luaL_Reg* functions, void (*destructor)(void*));
void* _luax_totype(struct lua_State* L, int index, uint64_t hash);
void* _luax_checktype(struct lua_State* L, int index, uint64_t hash, const char* debug);
int luax_typeerror(struct lua_State* L, int index, const char* expected);
void _luax_pushtype(struct lua_State* L, const char* name, uint64_t hash, void* object);
int _luax_checkenum(struct lua_State* L, int index, const StringEntry* map, const char* fallback, const char* label);
void luax_registerloader(struct lua_State* L, int (*loader)(struct lua_State* L), int index);
int luax_resume(struct lua_State* T, int n);
void luax_preload(lua_State* L);
void _luax_registertype(lua_State* L, const char* name, const luaL_Reg* functions, void (*destructor)(void*));
void* _luax_totype(lua_State* L, int index, uint64_t hash);
void* _luax_checktype(lua_State* L, int index, uint64_t hash, const char* debug);
int luax_typeerror(lua_State* L, int index, const char* expected);
void _luax_pushtype(lua_State* L, const char* name, uint64_t hash, void* object);
int _luax_checkenum(lua_State* L, int index, const StringEntry* map, const char* fallback, const char* label);
void luax_registerloader(lua_State* L, int (*loader)(lua_State* L), int index);
int luax_resume(lua_State* T, int n);
void luax_vthrow(void* L, const char* format, va_list args);
void luax_vlog(void* context, int level, const char* tag, const char* format, va_list args);
void luax_traceback(struct lua_State* L, struct lua_State* T, const char* message, int level);
int luax_getstack(struct lua_State* L);
void luax_pushconf(struct lua_State* L);
int luax_setconf(struct lua_State* L);
void luax_setmainthread(struct lua_State* L);
void luax_atexit(struct lua_State* L, void (*finalizer)(void));
uint32_t _luax_checku32(struct lua_State* L, int index);
uint32_t _luax_optu32(struct lua_State* L, int index, uint32_t fallback);
void luax_readcolor(struct lua_State* L, int index, float color[4]);
void luax_optcolor(struct lua_State* L, int index, float color[4]);
int luax_readmesh(struct lua_State* L, int index, float** vertices, uint32_t* vertexCount, uint32_t** indices, uint32_t* indexCount, bool* shouldFree);
void luax_traceback(lua_State* L, lua_State* T, const char* message, int level);
int luax_getstack(lua_State* L);
void luax_pushconf(lua_State* L);
int luax_setconf(lua_State* L);
void luax_setmainthread(lua_State* L);
void luax_atexit(lua_State* L, void (*finalizer)(void));
uint32_t _luax_checku32(lua_State* L, int index);
uint32_t _luax_optu32(lua_State* L, int index, uint32_t fallback);
void luax_readcolor(lua_State* L, int index, float color[4]);
void luax_optcolor(lua_State* L, int index, float color[4]);
int luax_readmesh(lua_State* L, int index, float** vertices, uint32_t* vertexCount, uint32_t** indices, uint32_t* indexCount, bool* shouldFree);
// Module helpers
#ifndef LOVR_DISABLE_DATA
struct Blob;
struct Image;
struct Blob* luax_readblob(struct lua_State* L, int index, const char* debug);
struct Image* luax_checkimage(struct lua_State* L, int index);
uint32_t luax_checkcodepoint(struct lua_State* L, int index);
struct Blob* luax_readblob(lua_State* L, int index, const char* debug);
struct Image* luax_checkimage(lua_State* L, int index);
uint32_t luax_checkcodepoint(lua_State* L, int index);
#endif
#ifndef LOVR_DISABLE_EVENT
struct Variant;
void luax_checkvariant(struct lua_State* L, int index, struct Variant* variant);
int luax_pushvariant(struct lua_State* L, struct Variant* variant);
void luax_checkvariant(lua_State* L, int index, struct Variant* variant);
int luax_pushvariant(lua_State* L, struct Variant* variant);
#endif
#ifndef LOVR_DISABLE_FILESYSTEM
@ -155,37 +154,37 @@ bool luax_writefile(const char* filename, const void* data, size_t size);
struct Buffer;
struct ColoredString;
struct Model;
struct Buffer* luax_checkbuffer(struct lua_State* L, int index);
void luax_readbufferfield(struct lua_State* L, int index, int type, void* data);
void luax_readbufferdata(struct lua_State* L, int index, struct Buffer* buffer, char* data);
uint32_t luax_checkcomparemode(struct lua_State* L, int index);
struct ColoredString* luax_checkcoloredstrings(struct lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
uint32_t luax_checknodeindex(struct lua_State* L, int index, struct Model* model);
struct Buffer* luax_checkbuffer(lua_State* L, int index);
void luax_readbufferfield(lua_State* L, int index, int type, void* data);
void luax_readbufferdata(lua_State* L, int index, struct Buffer* buffer, char* data);
uint32_t luax_checkcomparemode(lua_State* L, int index);
struct ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
uint32_t luax_checknodeindex(lua_State* L, int index, struct Model* model);
#endif
#ifndef LOVR_DISABLE_MATH
#include "math/pool.h" // TODO
float* luax_tovector(struct lua_State* L, int index, VectorType* type);
float* luax_checkvector(struct lua_State* L, int index, VectorType type, const char* expected);
float* luax_newtempvector(struct lua_State* L, VectorType type);
int luax_readvec3(struct lua_State* L, int index, float* v, const char* expected);
int luax_readscale(struct lua_State* L, int index, float* v, int components, const char* expected);
int luax_readquat(struct lua_State* L, int index, float* q, const char* expected);
int luax_readmat4(struct lua_State* L, int index, float* m, int scaleComponents);
uint64_t luax_checkrandomseed(struct lua_State* L, int index);
float* luax_tovector(lua_State* L, int index, VectorType* type);
float* luax_checkvector(lua_State* L, int index, VectorType type, const char* expected);
float* luax_newtempvector(lua_State* L, VectorType type);
int luax_readvec3(lua_State* L, int index, float* v, const char* expected);
int luax_readscale(lua_State* L, int index, float* v, int components, const char* expected);
int luax_readquat(lua_State* L, int index, float* q, const char* expected);
int luax_readmat4(lua_State* L, int index, float* m, int scaleComponents);
uint64_t luax_checkrandomseed(lua_State* L, int index);
#endif
#ifndef LOVR_DISABLE_PHYSICS
struct Joint;
struct Shape;
void luax_pushjoint(struct lua_State* L, struct Joint* joint);
void luax_pushshape(struct lua_State* L, struct Shape* shape);
struct Joint* luax_checkjoint(struct lua_State* L, int index);
struct Shape* luax_checkshape(struct lua_State* L, int index);
struct Shape* luax_newsphereshape(struct lua_State* L, int index);
struct Shape* luax_newboxshape(struct lua_State* L, int index);
struct Shape* luax_newcapsuleshape(struct lua_State* L, int index);
struct Shape* luax_newcylindershape(struct lua_State* L, int index);
struct Shape* luax_newmeshshape(struct lua_State* L, int index);
struct Shape* luax_newterrainshape(struct lua_State* L, int index);
void luax_pushjoint(lua_State* L, struct Joint* joint);
void luax_pushshape(lua_State* L, struct Shape* shape);
struct Joint* luax_checkjoint(lua_State* L, int index);
struct Shape* luax_checkshape(lua_State* L, int index);
struct Shape* luax_newsphereshape(lua_State* L, int index);
struct Shape* luax_newboxshape(lua_State* L, int index);
struct Shape* luax_newcapsuleshape(lua_State* L, int index);
struct Shape* luax_newcylindershape(lua_State* L, int index);
struct Shape* luax_newmeshshape(lua_State* L, int index);
struct Shape* luax_newterrainshape(lua_State* L, int index);
#endif

View File

@ -4,8 +4,6 @@
#include "data/sound.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
StringEntry lovrEffect[] = {

View File

@ -2,8 +2,6 @@
#include "audio/audio.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrSourceClone(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);

View File

@ -5,8 +5,6 @@
#include "data/sound.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrBlobGetName(lua_State* L) {
Blob* blob = luax_checktype(L, 1, Blob);
@ -24,7 +22,14 @@ static int l_lovrBlobGetSize(lua_State* L) {
static int l_lovrBlobGetString(lua_State* L) {
Blob* blob = luax_checktype(L, 1, Blob);
lua_pushlstring(L, blob->data, blob->size);
uint32_t offset = luax_optu32(L, 2, 0);
lovrCheck(offset < blob->size, "Blob byte offset must be less than the size of the Blob");
uint32_t length = luax_optu32(L, 3, blob->size - offset);
lovrCheck(length <= blob->size - offset, "Blob:getString range overflows the length of the Blob");
lua_pushlstring(L, (char*) blob->data + offset, length);
return 1;
}

View File

@ -2,8 +2,6 @@
#include "data/image.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
StringEntry lovrTextureFormat[] = {
[FORMAT_R8] = ENTRY("r8"),

View File

@ -2,8 +2,6 @@
#include "data/modelData.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static ModelNode* luax_checknode(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {

View File

@ -2,8 +2,6 @@
#include "data/rasterizer.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
uint32_t luax_checkcodepoint(lua_State* L, int index) {

View File

@ -2,8 +2,6 @@
#include "data/sound.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
StringEntry lovrSampleFormat[] = {
[SAMPLE_F32] = ENTRY("f32"),

View File

@ -2,8 +2,6 @@
#include "event/event.h"
#include "thread/thread.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>
@ -42,16 +40,23 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant) {
variant->value.number = lua_tonumber(L, index);
break;
case LUA_TSTRING:
variant->type = TYPE_STRING;
case LUA_TSTRING: {
size_t length;
const char* string = lua_tolstring(L, index, &length);
variant->value.string.pointer = malloc(length + 1);
lovrAssert(variant->value.string.pointer, "Out of memory");
memcpy(variant->value.string.pointer, string, length);
variant->value.string.pointer[length] = '\0';
variant->value.string.length = length;
if (length <= sizeof(variant->value.ministring.data)) {
variant->type = TYPE_MINISTRING;
variant->value.ministring.length = length;
memcpy(variant->value.ministring.data, string, length);
} else {
variant->type = TYPE_STRING;
variant->value.string.pointer = malloc(length + 1);
lovrAssert(variant->value.string.pointer, "Out of memory");
memcpy(variant->value.string.pointer, string, length);
variant->value.string.pointer[length] = '\0';
variant->value.string.length = length;
}
break;
}
case LUA_TUSERDATA:
variant->type = TYPE_OBJECT;
@ -82,6 +87,7 @@ int luax_pushvariant(lua_State* L, Variant* variant) {
case TYPE_BOOLEAN: lua_pushboolean(L, variant->value.boolean); return 1;
case TYPE_NUMBER: lua_pushnumber(L, variant->value.number); return 1;
case TYPE_STRING: lua_pushlstring(L, variant->value.string.pointer, variant->value.string.length); return 1;
case TYPE_MINISTRING: lua_pushlstring(L, variant->value.ministring.data, variant->value.ministring.length); return 1;
case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash64(variant->value.object.type, strlen(variant->value.object.type)), variant->value.object.pointer); return 1;
default: return 0;
}
@ -185,7 +191,7 @@ static int l_lovrEventPush(lua_State* L) {
}
static int l_lovrEventQuit(lua_State* L) {
int exitCode = luaL_optint(L, 1, 0);
int exitCode = luaL_optinteger(L, 1, 0);
Event event = { .type = EVENT_QUIT, .data.quit.exitCode = exitCode };
lovrEventPush(event);
return 0;

View File

@ -2,8 +2,6 @@
#include "filesystem/filesystem.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>

View File

@ -5,8 +5,6 @@
#include "data/modelData.h"
#include "data/rasterizer.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>
@ -1013,7 +1011,8 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
lua_getfield(L, index, "usage");
switch (lua_type(L, -1)) {
case LUA_TSTRING: info.usage = 1 << luax_checkenum(L, -1, TextureUsage, NULL); break;
case LUA_TTABLE: {
case LUA_TTABLE:
info.usage = 0;
int length = luax_len(L, -1);
for (int i = 0; i < length; i++) {
lua_rawgeti(L, -1, i + 1);
@ -1021,7 +1020,6 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
lua_pop(L, 1);
}
break;
}
case LUA_TNIL: break;
default: return luaL_error(L, "Expected Texture usage to be a string, table, or nil");
}
@ -1157,6 +1155,9 @@ static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage
source.code = blob->data;
source.size = blob->size;
*allocated = false;
} else {
*allocated = false;
return lovrGraphicsGetDefaultShaderSource(SHADER_UNLIT, stage);
}
ShaderSource bytecode = lovrGraphicsCompileShader(stage, &source);

View File

@ -2,8 +2,6 @@
#include "graphics/graphics.h"
#include "data/blob.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>

View File

@ -2,8 +2,6 @@
#include "graphics/graphics.h"
#include "data/rasterizer.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, ColoredString* stack) {

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrMaterialGetProperties(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);

View File

@ -3,8 +3,6 @@
#include "data/modelData.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
// This adds about 2-3us of overhead, which sucks, but the reduction in complexity is large
static int luax_callmodeldata(lua_State* L, const char* method, int nrets) {

View File

@ -4,8 +4,6 @@
#include "data/image.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>
@ -190,7 +188,7 @@ static int l_lovrPassSetProjection(lua_State* L) {
float up = luax_checkfloat(L, 5);
float down = luax_checkfloat(L, 6);
float clipNear = luax_optfloat(L, 7, .01f);
float clipFar = luax_optfloat(L, 8, 100.f);
float clipFar = luax_optfloat(L, 8, 0.f);
float matrix[16];
mat4_fov(matrix, left, right, up, down, clipNear, clipFar);
lovrPassSetProjection(pass, view, matrix);

View File

@ -3,8 +3,6 @@
#include "data/blob.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrReadbackIsComplete(lua_State* L) {
Readback* readback = luax_checktype(L, 1, Readback);

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrSamplerGetFilter(lua_State* L) {
Sampler* sampler = luax_checktype(L, 1, Sampler);

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
static int l_lovrShaderClone(lua_State* L) {

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrTallyGetType(lua_State* L) {
Tally* tally = luax_checktype(L, 1, Tally);

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "util.h"
#include "graphics/graphics.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrTextureNewView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
@ -116,6 +114,6 @@ const luaL_Reg lovrTexture[] = {
{ "getDimensions", l_lovrTextureGetDimensions },
{ "getMipmapCount", l_lovrTextureGetMipmapCount },
{ "getSampleCount", l_lovrTextureGetSampleCount },
{ "hasUsage ", l_lovrTextureHasUsage },
{ "hasUsage", l_lovrTextureHasUsage },
{ NULL, NULL }
};

View File

@ -3,8 +3,6 @@
#include "data/modelData.h"
#include "graphics/graphics.h"
#include "core/maf.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
StringEntry lovrHeadsetDriver[] = {
@ -650,8 +648,6 @@ int luaopen_lovr_headset(lua_State* L) {
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "headset");
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = luax_len(L, -1);
for (int i = 0; i < n; i++) {
@ -662,7 +658,6 @@ int luaopen_lovr_headset(lua_State* L) {
}
lua_pop(L, 1);
// Supersample
lua_getfield(L, -1, "supersample");
if (lua_type(L, -1) == LUA_TBOOLEAN) {
config.supersample = lua_toboolean(L, -1) ? 2.f : 1.f;
@ -671,27 +666,22 @@ int luaopen_lovr_headset(lua_State* L) {
}
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
config.offset = luax_optfloat(L, -1, 1.7f);
lua_pop(L, 1);
// Stencil
lua_getfield(L, -1, "stencil");
config.stencil = lua_toboolean(L, -1);
lua_pop(L, 1);
// Samples
lua_getfield(L, -1, "antialias");
config.antialias = lua_isnil(L, -1) ? true : lua_toboolean(L, -1);
lua_pop(L, 1);
// Depth
lua_getfield(L, -1, "submitdepth");
config.submitDepth = lua_toboolean(L, -1);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
config.overlay = lua_toboolean(L, -1);
lua_pop(L, 1);

View File

@ -1,7 +1,5 @@
#include "api.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrGetVersion(lua_State* L) {
lua_pushinteger(L, LOVR_VERSION_MAJOR);

View File

@ -4,8 +4,6 @@
#include "math/pool.h"
#include "math/randomGenerator.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
int l_lovrRandomGeneratorRandom(lua_State* L);

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "math/curve.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrCurveEvaluate(lua_State* L) {
Curve* curve = luax_checktype(L, 1, Curve);

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "math/randomGenerator.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
static double luax_checkrandomseedpart(lua_State* L, int index) {

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#define EQ_THRESHOLD 1e-10f
@ -1942,6 +1940,15 @@ static int l_lovrMat4Target(lua_State* L) {
return 1;
}
static int l_lovrMat4Reflect(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
vec3 position = luax_checkvector(L, 2, V_VEC3, NULL);
vec3 normal = luax_checkvector(L, 3, V_VEC3, NULL);
mat4_reflect(m, position, normal);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4__mul(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
VectorType type;
@ -2027,6 +2034,7 @@ const luaL_Reg lovrMat4[] = {
{ "fov", l_lovrMat4Fov },
{ "lookAt", l_lovrMat4LookAt },
{ "target", l_lovrMat4Target },
{ "reflect", l_lovrMat4Reflect },
{ "__mul", l_lovrMat4__mul },
{ "__tostring", l_lovrMat4__tostring },
{ "__newindex", l_lovrMat4__newindex },

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "physics/physics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
StringEntry lovrShapeType[] = {
[SHAPE_SPHERE] = ENTRY("sphere"),

View File

@ -2,8 +2,6 @@
#include "physics/physics.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdbool.h>
static int l_lovrColliderDestroy(lua_State* L) {

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "physics/physics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
void luax_pushjoint(lua_State* L, Joint* joint) {

View File

@ -3,8 +3,6 @@
#include "data/image.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "physics/physics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdbool.h>
#include <string.h>

View File

@ -3,8 +3,6 @@
#include "data/image.h"
#include "core/os.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
StringEntry lovrKeyboardKey[] = {

View File

@ -3,8 +3,6 @@
#include "thread/thread.h"
#include "thread/channel.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
#include <string.h>

View File

@ -2,8 +2,6 @@
#include "thread/channel.h"
#include "event/event.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
static void luax_checktimeout(lua_State* L, int index, double* timeout) {

View File

@ -1,8 +1,6 @@
#include "api.h"
#include "thread/thread.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrThreadStart(lua_State* L) {
Thread* thread = luax_checktype(L, 1, Thread);

View File

@ -1,7 +1,5 @@
#include "api.h"
#include "timer/timer.h"
#include <lua.h>
#include <lauxlib.h>
static int l_lovrTimerGetDelta(lua_State* L) {
lua_pushnumber(L, lovrTimerGetDelta());

View File

@ -197,7 +197,6 @@ typedef enum {
} gpu_slot_type;
enum {
GPU_STAGE_ALL = 0,
GPU_STAGE_VERTEX = (1 << 0),
GPU_STAGE_FRAGMENT = (1 << 1),
GPU_STAGE_COMPUTE = (1 << 2),

View File

@ -210,6 +210,8 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
static void gpu_release(gpu_memory* memory);
static void condemn(void* handle, VkObjectType type);
static void expunge(void);
static bool hasLayer(VkLayerProperties* layers, uint32_t count, const char* layer);
static bool hasExtension(VkExtensionProperties* extensions, uint32_t count, const char* extension);
static void createSwapchain(uint32_t width, uint32_t height);
static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool exact);
static VkFramebuffer getCachedFramebuffer(VkRenderPass pass, VkImageView images[9], uint32_t imageCount, uint32_t size[2]);
@ -226,6 +228,7 @@ static bool check(bool condition, const char* message);
// Functions that don't require an instance
#define GPU_FOREACH_ANONYMOUS(X)\
X(vkEnumerateInstanceLayerProperties)\
X(vkEnumerateInstanceExtensionProperties)\
X(vkCreateInstance)
@ -900,7 +903,7 @@ bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info) {
.binding = info->slots[i].number,
.descriptorType = types[info->slots[i].type],
.descriptorCount = 1,
.stageFlags = info->slots[i].stages == GPU_STAGE_ALL ? VK_SHADER_STAGE_ALL :
.stageFlags =
(((info->slots[i].stages & GPU_STAGE_VERTEX) ? VK_SHADER_STAGE_VERTEX_BIT : 0) |
((info->slots[i].stages & GPU_STAGE_FRAGMENT) ? VK_SHADER_STAGE_FRAGMENT_BIT : 0) |
((info->slots[i].stages & GPU_STAGE_COMPUTE) ? VK_SHADER_STAGE_COMPUTE_BIT : 0))
@ -1840,51 +1843,88 @@ bool gpu_init(gpu_config* config) {
GPU_FOREACH_ANONYMOUS(GPU_LOAD_ANONYMOUS);
{ // Instance
const char* extensions[32];
uint32_t extensionCount = 0;
struct {
bool validation;
bool portability;
bool debug;
} supports = { 0 };
if (state.config.vk.getInstanceExtensions) {
const char** instanceExtensions = state.config.vk.getInstanceExtensions(&extensionCount);
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < extensionCount; i++) {
extensions[i] = instanceExtensions[i];
// Layers
struct { const char* name; bool shouldEnable; bool* isEnabled; } layers[] = {
{ "VK_LAYER_KHRONOS_validation", config->debug, &supports.validation }
};
const char* enabledLayers[1];
uint32_t enabledLayerCount = 0;
VkLayerProperties layerInfo[32];
uint32_t count = COUNTOF(layerInfo);
VK(vkEnumerateInstanceLayerProperties(&count, layerInfo), "Failed to enumerate instance layers") return gpu_destroy(), false;
for (uint32_t i = 0; i < COUNTOF(layers); i++) {
if (!layers[i].shouldEnable) continue;
if (hasLayer(layerInfo, count, layers[i].name)) {
CHECK(enabledLayerCount < COUNTOF(enabledLayers), "Too many layers") return gpu_destroy(), false;
if (layers[i].isEnabled) *layers[i].isEnabled = true;
enabledLayers[enabledLayerCount++] = layers[i].name;
}
}
if (state.config.debug) {
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
extensions[extensionCount++] = "VK_EXT_debug_utils";
// Extensions
struct { const char* name; bool shouldEnable; bool* isEnabled; } extensions[] = {
{ "VK_KHR_portability_enumeration", true, &supports.portability },
{ "VK_EXT_debug_utils", config->debug, &supports.debug }
};
const char* enabledExtensions[32];
uint32_t enabledExtensionCount = 0;
if (state.config.vk.getInstanceExtensions) {
const char** instanceExtensions = state.config.vk.getInstanceExtensions(&enabledExtensionCount);
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many instance extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < enabledExtensionCount; i++) {
enabledExtensions[i] = instanceExtensions[i];
}
}
VkExtensionProperties extensionInfo[256];
uint32_t count = COUNTOF(extensionInfo);
count = COUNTOF(extensionInfo);
VK(vkEnumerateInstanceExtensionProperties(NULL, &count, extensionInfo), "Failed to enumerate instance extensions") return gpu_destroy(), false;
VkInstanceCreateFlags instanceFlags = 0;
#ifdef VK_KHR_portability_enumeration
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensionInfo[i].extensionName, "VK_KHR_portability_enumeration")) {
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
extensions[extensionCount++] = "VK_KHR_portability_enumeration";
instanceFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
for (uint32_t i = 0; i < COUNTOF(extensions); i++) {
if (!extensions[i].shouldEnable) continue;
if (hasExtension(extensionInfo, count, extensions[i].name)) {
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many instance extensions") return gpu_destroy(), false;
if (extensions[i].isEnabled) *extensions[i].isEnabled = true;
enabledExtensions[enabledExtensionCount++] = extensions[i].name;
}
}
#endif
if (state.config.debug && !supports.validation && state.config.callback) {
state.config.callback(state.config.userdata, "Warning: GPU debug mode is enabled, but validation layer is not supported", false);
}
if (state.config.debug && !supports.debug) {
state.config.debug = false;
}
VkInstanceCreateInfo instanceInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.flags = instanceFlags,
#ifdef VK_KHR_portability_enumeration
.flags = supports.portability ? VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR : 0,
#endif
.pApplicationInfo = &(VkApplicationInfo) {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pEngineName = config->engineName,
.engineVersion = VK_MAKE_VERSION(config->engineVersion[0], config->engineVersion[1], config->engineVersion[2]),
.apiVersion = VK_MAKE_VERSION(1, 1, 0)
},
.enabledLayerCount = state.config.debug ? 1 : 0,
.ppEnabledLayerNames = (const char*[]) { "VK_LAYER_KHRONOS_validation" },
.enabledExtensionCount = extensionCount,
.ppEnabledExtensionNames = extensions
.enabledLayerCount = enabledLayerCount,
.ppEnabledLayerNames = enabledLayers,
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensions
};
if (state.config.vk.createInstance) {
@ -2056,23 +2096,33 @@ bool gpu_init(gpu_config* config) {
}
CHECK(state.queueFamilyIndex != ~0u, "Queue selection failed") return gpu_destroy(), false;
const char* extensions[4];
uint32_t extensionCount = 0;
struct {
bool swapchain;
} supports = { 0 };
if (state.surface) {
extensions[extensionCount++] = "VK_KHR_swapchain";
}
struct { const char* name; bool shouldEnable; bool* isEnabled; } extensions[] = {
{ "VK_KHR_swapchain", state.surface, &supports.swapchain },
{ "VK_KHR_portability_subset", true, NULL }
};
const char* enabledExtensions[4];
uint32_t enabledExtensionCount = 0;
VkExtensionProperties extensionInfo[256];
uint32_t count = COUNTOF(extensionInfo);
VK(vkEnumerateDeviceExtensionProperties(state.adapter, NULL, &count, extensionInfo), "Failed to enumerate device extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensionInfo[i].extensionName, "VK_KHR_portability_subset")) {
extensions[extensionCount++] = "VK_KHR_portability_subset";
for (uint32_t i = 0; i < COUNTOF(extensions); i++) {
if (!extensions[i].shouldEnable) continue;
if (hasExtension(extensionInfo, count, extensions[i].name)) {
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many device extensions") return gpu_destroy(), false;
if (extensions[i].isEnabled) *extensions[i].isEnabled = true;
enabledExtensions[enabledExtensionCount++] = extensions[i].name;
}
}
CHECK(supports.swapchain || !state.surface, "Swapchain extension not supported") return gpu_destroy(), false;
VkDeviceCreateInfo deviceInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = config->features ? &enabledFeatures : NULL,
@ -2083,8 +2133,8 @@ bool gpu_init(gpu_config* config) {
.pQueuePriorities = &(float) { 1.f },
.queueCount = 1
},
.enabledExtensionCount = extensionCount,
.ppEnabledExtensionNames = extensions
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensions
};
if (state.config.vk.createDevice) {
@ -2325,8 +2375,9 @@ void gpu_destroy(void) {
state.tick[GPU] = state.tick[CPU];
expunge();
if (state.pipelineCache) vkDestroyPipelineCache(state.device, state.pipelineCache, NULL);
if (state.scratchpad[0].buffer) vkDestroyBuffer(state.device, state.scratchpad[0].buffer, NULL);
if (state.scratchpad[1].buffer) vkDestroyBuffer(state.device, state.scratchpad[1].buffer, NULL);
for (uint32_t i = 0; i < COUNTOF(state.scratchpad); i++) {
if (state.scratchpad[i].buffer) vkDestroyBuffer(state.device, state.scratchpad[i].buffer, NULL);
}
for (uint32_t i = 0; i < COUNTOF(state.ticks); i++) {
gpu_tick* tick = &state.ticks[i];
if (tick->pool) vkDestroyCommandPool(state.device, tick->pool, NULL);
@ -2529,6 +2580,8 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
memory->handle = NULL;
return NULL;
}
} else {
memory->pointer = NULL;
}
allocator->block = memory;
@ -2586,6 +2639,24 @@ static void expunge() {
}
}
static bool hasLayer(VkLayerProperties* layers, uint32_t count, const char* layer) {
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(layers[i].layerName, layer)) {
return true;
}
}
return false;
}
static bool hasExtension(VkExtensionProperties* extensions, uint32_t count, const char* extension) {
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensions[i].extensionName, extension)) {
return true;
}
}
return false;
}
static void createSwapchain(uint32_t width, uint32_t height) {
if (width == 0 || height == 0) {
state.swapchainValid = false;

View File

@ -1,10 +0,0 @@
#include "gpu.h"
#include <webgpu/webgpu.h>
bool gpu_init(gpu_config* config) {
return false;
}
void gpu_destroy(void) {
//
}

552
src/core/gpu_wgpu.c Normal file
View File

@ -0,0 +1,552 @@
#include "gpu.h"
#include <webgpu/webgpu.h>
#include <emscripten/html5_webgpu.h>
#include <string.h>
struct gpu_buffer {
WGPUBuffer handle;
};
struct gpu_texture {
WGPUTexture handle;
WGPUTextureView view;
};
struct gpu_sampler {
WGPUSampler handle;
};
struct gpu_layout {
WGPUBindGroupLayout handle;
};
struct gpu_shader {
WGPUShaderModule handles[2];
WGPUPipelineLayout pipelineLayout;
};
struct gpu_pipeline {
WGPURenderPipeline render;
WGPUComputePipeline compute;
};
size_t gpu_sizeof_buffer(void) { return sizeof(gpu_buffer); }
size_t gpu_sizeof_texture(void) { return sizeof(gpu_texture); }
size_t gpu_sizeof_sampler(void) { return sizeof(gpu_sampler); }
size_t gpu_sizeof_layout(void) { return sizeof(gpu_layout); }
size_t gpu_sizeof_shader(void) { return sizeof(gpu_shader); }
// State
static struct {
WGPUDevice device;
} state;
// Helpers
#define COUNTOF(x) (sizeof(x) / sizeof(x[0]))
static WGPUTextureFormat convertFormat(gpu_texture_format format, bool srgb);
// Buffer
bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) {
return buffer->handle = wgpuDeviceCreateBuffer(state.device, &(WGPUBufferDescriptor) {
.label = info->label,
.usage =
WGPUBufferUsage_Vertex |
WGPUBufferUsage_Index |
WGPUBufferUsage_Uniform |
WGPUBufferUsage_Storage |
WGPUBufferUsage_Indirect |
WGPUBufferUsage_CopySrc |
WGPUBufferUsage_CopyDst |
WGPUBufferUsage_QueryResolve,
.size = info->size,
.mappedAtCreation = !!info->pointer
});
}
void gpu_buffer_destroy(gpu_buffer* buffer) {
wgpuBufferDestroy(buffer->handle);
}
// Texture
bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
static const WGPUTextureDimension dimensions[] = {
[GPU_TEXTURE_2D] = WGPUTextureDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureDimension_2D,
[GPU_TEXTURE_ARRAY] = WGPUTextureDimension_2D
};
static const WGPUTextureViewDimension types[] = {
[GPU_TEXTURE_2D] = WGPUTextureViewDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureViewDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureViewDimension_Cube,
[GPU_TEXTURE_ARRAY] = WGPUTextureViewDimension_2DArray
};
texture->handle = wgpuDeviceCreateTexture(state.device, &(WGPUTextureDescriptor) {
.label = info->label,
.usage =
((info->usage & GPU_TEXTURE_RENDER) ? WGPUTextureUsage_RenderAttachment : 0) |
((info->usage & GPU_TEXTURE_SAMPLE) ? WGPUTextureUsage_TextureBinding : 0) |
((info->usage & GPU_TEXTURE_STORAGE) ? WGPUTextureUsage_StorageBinding : 0) |
((info->usage & GPU_TEXTURE_COPY_SRC) ? WGPUTextureUsage_CopySrc : 0) |
((info->usage & GPU_TEXTURE_COPY_DST) ? WGPUTextureUsage_CopyDst : 0),
.dimension = dimensions[info->type],
.size = { info->size[0], info->size[1], info->size[2] },
.format = convertFormat(info->format, info->srgb),
.mipLevelCount = info->mipmaps,
.sampleCount = info->samples
});
texture->view = wgpuTextureCreateView(texture->handle, &(WGPUTextureViewDescriptor) {
.format = wgpuTextureGetFormat(texture->handle),
.dimension = types[info->type],
.mipLevelCount = info->mipmaps,
.arrayLayerCount = info->type == GPU_TEXTURE_ARRAY ? info->size[2] : 1
});
// TODO upload, mipgen
return true;
}
bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
static const WGPUTextureViewDimension types[] = {
[GPU_TEXTURE_2D] = WGPUTextureViewDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureViewDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureViewDimension_Cube,
[GPU_TEXTURE_ARRAY] = WGPUTextureViewDimension_2DArray
};
texture->handle = NULL;
return texture->view = wgpuTextureCreateView(info->source->handle, &(WGPUTextureViewDescriptor) {
.format = wgpuTextureGetFormat(info->source->handle),
.dimension = types[info->type],
.baseMipLevel = info->levelIndex,
.mipLevelCount = info->levelCount,
.baseArrayLayer = info->layerIndex,
.arrayLayerCount = info->layerCount
});
}
void gpu_texture_destroy(gpu_texture* texture) {
wgpuTextureViewRelease(texture->view);
wgpuTextureDestroy(texture->handle);
}
// Sampler
bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info) {
static const WGPUFilterMode filters[] = {
[GPU_FILTER_NEAREST] = WGPUFilterMode_Nearest,
[GPU_FILTER_LINEAR] = WGPUFilterMode_Linear
};
static const WGPUAddressMode wraps[] = {
[GPU_WRAP_CLAMP] = WGPUAddressMode_ClampToEdge,
[GPU_WRAP_REPEAT] = WGPUAddressMode_Repeat,
[GPU_WRAP_MIRROR] = WGPUAddressMode_MirrorRepeat
};
static const WGPUCompareFunction compares[] = {
[GPU_COMPARE_NONE] = WGPUCompareFunction_Always,
[GPU_COMPARE_EQUAL] = WGPUCompareFunction_Equal,
[GPU_COMPARE_NEQUAL] = WGPUCompareFunction_NotEqual,
[GPU_COMPARE_LESS] = WGPUCompareFunction_Less,
[GPU_COMPARE_LEQUAL] = WGPUCompareFunction_LessEqual,
[GPU_COMPARE_GREATER] = WGPUCompareFunction_Greater,
[GPU_COMPARE_GEQUAL] = WGPUCompareFunction_GreaterEqual
};
return sampler->handle = wgpuDeviceCreateSampler(state.device, &(WGPUSamplerDescriptor) {
.addressModeU = wraps[info->wrap[0]],
.addressModeV = wraps[info->wrap[1]],
.addressModeW = wraps[info->wrap[2]],
.magFilter = filters[info->mag],
.minFilter = filters[info->min],
.mipmapFilter = filters[info->mip],
.lodMinClamp = info->lodClamp[0],
.lodMaxClamp = info->lodClamp[1],
.compare = compares[info->compare],
.maxAnisotropy = info->anisotropy
});
}
void gpu_sampler_destroy(gpu_sampler* sampler) {
wgpuSamplerRelease(sampler->handle);
}
// Layout
bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info) {
static const WGPUBufferBindingType bufferTypes[] = {
[GPU_SLOT_UNIFORM_BUFFER] = WGPUBufferBindingType_Uniform,
[GPU_SLOT_STORAGE_BUFFER] = WGPUBufferBindingType_Storage,
[GPU_SLOT_UNIFORM_BUFFER_DYNAMIC] = WGPUBufferBindingType_Uniform,
[GPU_SLOT_STORAGE_BUFFER_DYNAMIC] = WGPUBufferBindingType_Storage
};
gpu_slot* slot = info->slots;
WGPUBindGroupLayoutEntry entries[32];
for (uint32_t i = 0; i < info->count; i++, slot++) {
entries[i] = (WGPUBindGroupLayoutEntry) {
.binding = slot->number,
.visibility =
(((slot->stages & GPU_STAGE_VERTEX) ? WGPUShaderStage_Vertex : 0) |
((slot->stages & GPU_STAGE_FRAGMENT) ? WGPUShaderStage_Fragment : 0) |
((slot->stages & GPU_STAGE_COMPUTE) ? WGPUShaderStage_Compute : 0))
};
switch (info->slots[i].type) {
case GPU_SLOT_UNIFORM_BUFFER_DYNAMIC:
case GPU_SLOT_STORAGE_BUFFER_DYNAMIC:
entries[i].buffer.hasDynamicOffset = true;
/* fallthrough */
case GPU_SLOT_UNIFORM_BUFFER:
case GPU_SLOT_STORAGE_BUFFER:
entries[i].buffer.type = bufferTypes[slot->type];
break;
// FIXME need more metadata
case GPU_SLOT_SAMPLED_TEXTURE:
entries[i].texture.sampleType = WGPUTextureSampleType_Float;
entries[i].texture.viewDimension = WGPUTextureViewDimension_2D;
entries[i].texture.multisampled = false;
break;
// FIXME need more metadata
case GPU_SLOT_STORAGE_TEXTURE:
entries[i].storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
entries[i].storageTexture.format = WGPUTextureFormat_Undefined;
entries[i].storageTexture.viewDimension = WGPUTextureViewDimension_2D;
break;
// FIXME need more metadata?
case GPU_SLOT_SAMPLER:
entries[i].sampler.type = WGPUSamplerBindingType_Filtering;
break;
}
}
return layout->handle = wgpuDeviceCreateBindGroupLayout(state.device, &(WGPUBindGroupLayoutDescriptor) {
.entryCount = info->count,
.entries = entries
});
}
void gpu_layout_destroy(gpu_layout* layout) {
wgpuBindGroupLayoutRelease(layout->handle);
}
// Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) {
for (uint32_t i = 0; i < COUNTOF(info->stages) && info->stages[i].code; i++) {
WGPUShaderModuleSPIRVDescriptor spirv = {
.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor,
.codeSize = info->stages[i].length,
.code = info->stages[i].code
};
shader->handles[i] = wgpuDeviceCreateShaderModule(state.device, &(WGPUShaderModuleDescriptor) {
.nextInChain = &spirv.chain,
.label = info->label
});
}
uint32_t layoutCount = 0;
WGPUBindGroupLayout layouts[4];
for (uint32_t i = 0; i < COUNTOF(info->layouts) && info->layouts[i]; i++) {
layouts[layoutCount++] = info->layouts[i]->handle;
}
return shader->pipelineLayout = wgpuDeviceCreatePipelineLayout(state.device, &(WGPUPipelineLayoutDescriptor) {
.bindGroupLayoutCount = layoutCount,
.bindGroupLayouts = layouts
});
}
void gpu_shader_destroy(gpu_shader* shader) {
wgpuShaderModuleRelease(shader->handles[0]);
wgpuShaderModuleRelease(shader->handles[1]);
wgpuPipelineLayoutRelease(shader->pipelineLayout);
}
// Bundle
// Pipeline
bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info) {
static const WGPUPrimitiveTopology topologies[] = {
[GPU_DRAW_POINTS] = WGPUPrimitiveTopology_PointList,
[GPU_DRAW_LINES] = WGPUPrimitiveTopology_LineList,
[GPU_DRAW_TRIANGLES] = WGPUPrimitiveTopology_TriangleList
};
static const WGPUVertexFormat attributeTypes[] = {
[GPU_TYPE_I8x4] = WGPUVertexFormat_Sint8x4,
[GPU_TYPE_U8x4] = WGPUVertexFormat_Uint8x4,
[GPU_TYPE_SN8x4] = WGPUVertexFormat_Snorm8x4,
[GPU_TYPE_UN8x4] = WGPUVertexFormat_Unorm8x4,
[GPU_TYPE_UN10x3] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16x2] = WGPUVertexFormat_Sint16x2,
[GPU_TYPE_I16x4] = WGPUVertexFormat_Sint16x4,
[GPU_TYPE_U16] = WGPUVertexFormat_Undefined,
[GPU_TYPE_U16x2] = WGPUVertexFormat_Uint16x2,
[GPU_TYPE_U16x4] = WGPUVertexFormat_Uint16x4,
[GPU_TYPE_SN16x2] = WGPUVertexFormat_Snorm16x2,
[GPU_TYPE_SN16x4] = WGPUVertexFormat_Snorm16x4,
[GPU_TYPE_UN16x2] = WGPUVertexFormat_Unorm16x2,
[GPU_TYPE_UN16x4] = WGPUVertexFormat_Unorm16x4,
[GPU_TYPE_I32] = WGPUVertexFormat_Sint32,
[GPU_TYPE_I32x2] = WGPUVertexFormat_Sint32x2,
[GPU_TYPE_I32x3] = WGPUVertexFormat_Sint32x3,
[GPU_TYPE_I32x4] = WGPUVertexFormat_Sint32x4,
[GPU_TYPE_U32] = WGPUVertexFormat_Uint32,
[GPU_TYPE_U32x2] = WGPUVertexFormat_Uint32x2,
[GPU_TYPE_U32x3] = WGPUVertexFormat_Uint32x3,
[GPU_TYPE_U32x4] = WGPUVertexFormat_Uint32x4,
[GPU_TYPE_F16x2] = WGPUVertexFormat_Float16x2,
[GPU_TYPE_F16x4] = WGPUVertexFormat_Float16x4,
[GPU_TYPE_F32] = WGPUVertexFormat_Float32,
[GPU_TYPE_F32x2] = WGPUVertexFormat_Float32x2,
[GPU_TYPE_F32x3] = WGPUVertexFormat_Float32x3,
[GPU_TYPE_F32x4] = WGPUVertexFormat_Float32x4
};
static const WGPUFrontFace frontFaces[] = {
[GPU_WINDING_CCW] = WGPUFrontFace_CCW,
[GPU_WINDING_CW] = WGPUFrontFace_CW
};
static const WGPUCullMode cullModes[] = {
[GPU_CULL_NONE] = WGPUCullMode_None,
[GPU_CULL_FRONT] = WGPUCullMode_Front,
[GPU_CULL_BACK] = WGPUCullMode_Back
};
static const WGPUCompareFunction compares[] = {
[GPU_COMPARE_NONE] = WGPUCompareFunction_Always,
[GPU_COMPARE_EQUAL] = WGPUCompareFunction_Equal,
[GPU_COMPARE_NEQUAL] = WGPUCompareFunction_NotEqual,
[GPU_COMPARE_LESS] = WGPUCompareFunction_Less,
[GPU_COMPARE_LEQUAL] = WGPUCompareFunction_LessEqual,
[GPU_COMPARE_GREATER] = WGPUCompareFunction_Greater,
[GPU_COMPARE_GEQUAL] = WGPUCompareFunction_GreaterEqual
};
static const WGPUStencilOperation stencilOps[] = {
[GPU_STENCIL_KEEP] = WGPUStencilOperation_Keep,
[GPU_STENCIL_ZERO] = WGPUStencilOperation_Zero,
[GPU_STENCIL_REPLACE] = WGPUStencilOperation_Replace,
[GPU_STENCIL_INCREMENT] = WGPUStencilOperation_IncrementClamp,
[GPU_STENCIL_DECREMENT] = WGPUStencilOperation_DecrementClamp,
[GPU_STENCIL_INCREMENT_WRAP] = WGPUStencilOperation_IncrementWrap,
[GPU_STENCIL_DECREMENT_WRAP] = WGPUStencilOperation_DecrementWrap,
[GPU_STENCIL_INVERT] = WGPUStencilOperation_Invert
};
static const WGPUBlendFactor blendFactors[] = {
[GPU_BLEND_ZERO] = WGPUBlendFactor_Zero,
[GPU_BLEND_ONE] = WGPUBlendFactor_One,
[GPU_BLEND_SRC_COLOR] = WGPUBlendFactor_Src,
[GPU_BLEND_ONE_MINUS_SRC_COLOR] = WGPUBlendFactor_OneMinusSrc,
[GPU_BLEND_SRC_ALPHA] = WGPUBlendFactor_SrcAlpha,
[GPU_BLEND_ONE_MINUS_SRC_ALPHA] = WGPUBlendFactor_OneMinusSrcAlpha,
[GPU_BLEND_DST_COLOR] = WGPUBlendFactor_Dst,
[GPU_BLEND_ONE_MINUS_DST_COLOR] = WGPUBlendFactor_OneMinusDst,
[GPU_BLEND_DST_ALPHA] = WGPUBlendFactor_DstAlpha,
[GPU_BLEND_ONE_MINUS_DST_ALPHA] = WGPUBlendFactor_OneMinusDstAlpha
};
static const WGPUBlendOperation blendOps[] = {
[GPU_BLEND_ADD] = WGPUBlendOperation_Add,
[GPU_BLEND_SUB] = WGPUBlendOperation_Subtract,
[GPU_BLEND_RSUB] = WGPUBlendOperation_ReverseSubtract,
[GPU_BLEND_MIN] = WGPUBlendOperation_Min,
[GPU_BLEND_MAX] = WGPUBlendOperation_Max
};
uint32_t totalAttributeCount = 0;
WGPUVertexAttribute attributes[16];
WGPUVertexBufferLayout vertexBuffers[16];
for (uint32_t i = 0; i < info->vertex.bufferCount; i++) {
vertexBuffers[i] = (WGPUVertexBufferLayout) {
.arrayStride = info->vertex.bufferStrides[i],
.stepMode = (info->vertex.instancedBuffers & (1 << i)) ? WGPUVertexStepMode_Instance : WGPUVertexStepMode_Vertex,
.attributeCount = 0,
.attributes = &attributes[totalAttributeCount]
};
for (uint32_t j = 0; j < info->vertex.attributeCount; j++) {
if (info->vertex.attributes[j].buffer == i) {
attributes[totalAttributeCount++] = (WGPUVertexAttribute) {
.format = attributeTypes[info->vertex.attributes[j].type],
.offset = info->vertex.attributes[j].offset,
.shaderLocation = info->vertex.attributes[j].location
};
}
}
totalAttributeCount += vertexBuffers[i].attributeCount;
}
WGPUVertexState vertex = {
.module = info->shader->handles[0],
.entryPoint = "main",
.bufferCount = info->vertex.bufferCount,
.buffers = vertexBuffers
};
WGPUPrimitiveState primitive = {
.topology = topologies[info->drawMode],
.frontFace = frontFaces[info->rasterizer.winding],
.cullMode = cullModes[info->rasterizer.cullMode],
};
WGPUStencilFaceState stencil = {
.compare = compares[info->stencil.test],
.failOp = stencilOps[info->stencil.failOp],
.depthFailOp = stencilOps[info->stencil.depthFailOp],
.passOp = stencilOps[info->stencil.passOp]
};
WGPUDepthStencilState depth = {
.format = convertFormat(info->depth.format, false),
.depthWriteEnabled = info->depth.write,
.depthCompare = compares[info->depth.test],
.stencilFront = stencil,
.stencilBack = stencil,
.stencilReadMask = info->stencil.testMask,
.stencilWriteMask = info->stencil.writeMask,
.depthBias = info->rasterizer.depthOffset,
.depthBiasSlopeScale = info->rasterizer.depthOffsetSloped,
.depthBiasClamp = info->rasterizer.depthOffsetClamp
};
WGPUMultisampleState multisample = {
.count = info->multisample.count,
.alphaToCoverageEnabled = info->multisample.alphaToCoverage
};
WGPUBlendState blends[4];
WGPUColorTargetState targets[4];
for (uint32_t i = 0; i < info->attachmentCount; i++) {
targets[i] = (WGPUColorTargetState) {
.format = convertFormat(info->color[i].format, info->color[i].srgb),
.blend = info->color[i].blend.enabled ? &blends[i] : NULL,
.writeMask = info->color[i].mask
};
if (info->color[i].blend.enabled) {
blends[i] = (WGPUBlendState) {
.color.operation = blendOps[info->color[i].blend.color.op],
.color.srcFactor = blendFactors[info->color[i].blend.color.src],
.color.dstFactor = blendFactors[info->color[i].blend.color.dst],
.alpha.operation = blendOps[info->color[i].blend.alpha.op],
.alpha.srcFactor = blendFactors[info->color[i].blend.alpha.src],
.alpha.dstFactor = blendFactors[info->color[i].blend.alpha.dst]
};
}
}
WGPUFragmentState fragment = {
.module = info->shader->handles[1],
.entryPoint = "main",
.targetCount = info->attachmentCount,
.targets = targets
};
WGPURenderPipelineDescriptor pipelineInfo = {
.label = info->label,
.layout = info->shader->pipelineLayout,
.vertex = vertex,
.primitive = primitive,
.depthStencil = info->depth.format ? &depth : NULL,
.multisample = multisample,
.fragment = &fragment
};
return pipeline->render = wgpuDeviceCreateRenderPipeline(state.device, &pipelineInfo);
}
bool gpu_pipeline_init_compute(gpu_pipeline* pipeline, gpu_compute_pipeline_info* info) {
}
void gpu_pipeline_destroy(gpu_pipeline* pipeline) {
if (pipeline->render) wgpuRenderPipelineRelease(pipeline->render);
if (pipeline->compute) wgpuComputePipelineRelease(pipeline->compute);
}
// Entry
bool gpu_init(gpu_config* config) {
state.device = emscripten_webgpu_get_device();
return !!state.device;
}
void gpu_destroy(void) {
if (state.device) wgpuDeviceDestroy(state.device);
memset(&state, 0, sizeof(state));
}
// Helpers
static WGPUTextureFormat convertFormat(gpu_texture_format format, bool srgb) {
static const WGPUTextureFormat formats[][2] = {
[GPU_FORMAT_R8] = { WGPUTextureFormat_R8Unorm, WGPUTextureFormat_R8Unorm },
[GPU_FORMAT_RG8] = { WGPUTextureFormat_RG8Unorm, WGPUTextureFormat_RG8Unorm },
[GPU_FORMAT_RGBA8] = { WGPUTextureFormat_RGBA8Unorm, WGPUTextureFormat_RGBA8UnormSrgb },
[GPU_FORMAT_R16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RG16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGBA16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_R16F] = { WGPUTextureFormat_R16Float, WGPUTextureFormat_R16Float },
[GPU_FORMAT_RG16F] = { WGPUTextureFormat_RG16Float, WGPUTextureFormat_RG16Float },
[GPU_FORMAT_RGBA16F] = { WGPUTextureFormat_RGBA16Float, WGPUTextureFormat_RGBA16Float },
[GPU_FORMAT_R32F] = { WGPUTextureFormat_R32Float, WGPUTextureFormat_R32Float },
[GPU_FORMAT_RG32F] = { WGPUTextureFormat_RG32Float, WGPUTextureFormat_RG32Float },
[GPU_FORMAT_RGBA32F] = { WGPUTextureFormat_RGBA32Float, WGPUTextureFormat_RGBA32Float },
[GPU_FORMAT_RGB565] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGB5A1] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGB10A2] = {WGPUTextureFormat_RGB10A2Unorm, WGPUTextureFormat_RGB10A2Unorm },
[GPU_FORMAT_RG11B10F] = { WGPUTextureFormat_RG11B10Ufloat, WGPUTextureFormat_RG11B10Ufloat },
[GPU_FORMAT_D16] = { WGPUTextureFormat_Depth16Unorm, WGPUTextureFormat_Depth16Unorm },
[GPU_FORMAT_D32F] = { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Depth32Float },
[GPU_FORMAT_D24S8] = { WGPUTextureFormat_Depth24PlusStencil8, WGPUTextureFormat_Depth24PlusStencil8 },
[GPU_FORMAT_D32FS8] = { WGPUTextureFormat_Depth32FloatStencil8, WGPUTextureFormat_Depth32FloatStencil8 },
[GPU_FORMAT_BC1] = { WGPUTextureFormat_BC1RGBAUnorm, WGPUTextureFormat_BC1RGBAUnormSrgb },
[GPU_FORMAT_BC2] = { WGPUTextureFormat_BC2RGBAUnorm, WGPUTextureFormat_BC2RGBAUnormSrgb },
[GPU_FORMAT_BC3] = { WGPUTextureFormat_BC3RGBAUnorm, WGPUTextureFormat_BC3RGBAUnormSrgb },
[GPU_FORMAT_BC4U] = { WGPUTextureFormat_BC4RUnorm, WGPUTextureFormat_BC4RUnorm },
[GPU_FORMAT_BC4S] = { WGPUTextureFormat_BC4RSnorm, WGPUTextureFormat_BC4RSnorm },
[GPU_FORMAT_BC5U] = { WGPUTextureFormat_BC5RGUnorm, WGPUTextureFormat_BC5RGUnorm },
[GPU_FORMAT_BC5S] = { WGPUTextureFormat_BC5RGSnorm, WGPUTextureFormat_BC5RGSnorm },
[GPU_FORMAT_BC6UF] = { WGPUTextureFormat_BC6HRGBUfloat, WGPUTextureFormat_BC6HRGBUfloat },
[GPU_FORMAT_BC6SF] = { WGPUTextureFormat_BC6HRGBFloat, WGPUTextureFormat_BC6HRGBFloat },
[GPU_FORMAT_BC7] = { WGPUTextureFormat_BC7RGBAUnorm, WGPUTextureFormat_BC7RGBAUnormSrgb },
[GPU_FORMAT_ASTC_4x4] = { WGPUTextureFormat_ASTC4x4Unorm, WGPUTextureFormat_ASTC4x4UnormSrgb },
[GPU_FORMAT_ASTC_5x4] = { WGPUTextureFormat_ASTC5x4Unorm, WGPUTextureFormat_ASTC5x4UnormSrgb },
[GPU_FORMAT_ASTC_5x5] = { WGPUTextureFormat_ASTC5x5Unorm, WGPUTextureFormat_ASTC5x5UnormSrgb },
[GPU_FORMAT_ASTC_6x5] = { WGPUTextureFormat_ASTC6x5Unorm, WGPUTextureFormat_ASTC6x5UnormSrgb },
[GPU_FORMAT_ASTC_6x6] = { WGPUTextureFormat_ASTC6x6Unorm, WGPUTextureFormat_ASTC6x6UnormSrgb },
[GPU_FORMAT_ASTC_8x5] = { WGPUTextureFormat_ASTC8x5Unorm, WGPUTextureFormat_ASTC8x5UnormSrgb },
[GPU_FORMAT_ASTC_8x6] = { WGPUTextureFormat_ASTC8x6Unorm, WGPUTextureFormat_ASTC8x6UnormSrgb },
[GPU_FORMAT_ASTC_8x8] = { WGPUTextureFormat_ASTC8x8Unorm, WGPUTextureFormat_ASTC8x8UnormSrgb },
[GPU_FORMAT_ASTC_10x5] = { WGPUTextureFormat_ASTC10x5Unorm, WGPUTextureFormat_ASTC10x5UnormSrgb },
[GPU_FORMAT_ASTC_10x6] = { WGPUTextureFormat_ASTC10x6Unorm, WGPUTextureFormat_ASTC10x6UnormSrgb },
[GPU_FORMAT_ASTC_10x8] = { WGPUTextureFormat_ASTC10x8Unorm, WGPUTextureFormat_ASTC10x8UnormSrgb },
[GPU_FORMAT_ASTC_10x10] = { WGPUTextureFormat_ASTC10x10Unorm, WGPUTextureFormat_ASTC10x10UnormSrgb },
[GPU_FORMAT_ASTC_12x10] = { WGPUTextureFormat_ASTC12x10Unorm, WGPUTextureFormat_ASTC12x10UnormSrgb },
[GPU_FORMAT_ASTC_12x12] = { WGPUTextureFormat_ASTC12x12Unorm, WGPUTextureFormat_ASTC12x12UnormSrgb }
};
return formats[format][srgb];
}

View File

@ -724,6 +724,27 @@ MAF mat4 mat4_target(mat4 m, vec3 from, vec3 to, vec3 up) {
return m;
}
MAF mat4 mat4_reflect(mat4 m, vec3 p, vec3 n) {
float d = vec3_dot(p, n);
m[0] = -2.f * n[0] * n[0] + 1.f;
m[1] = -2.f * n[0] * n[1];
m[2] = -2.f * n[0] * n[2];
m[3] = 0.f;
m[4] = -2.f * n[1] * n[0];
m[5] = -2.f * n[1] * n[1] + 1.f;
m[6] = -2.f * n[1] * n[2];
m[7] = 0.f;
m[8] = -2.f * n[2] * n[0];
m[9] = -2.f * n[2] * n[1];
m[10] = -2.f * n[2] * n[2] + 1.f;
m[11] = 0.f;
m[12] = 2.f * d * n[0];
m[13] = 2.f * d * n[1];
m[14] = 2.f * d * n[2];
m[15] = 1.f;
return m;
}
// Apply matrix to a vec3
// Difference from mat4_mulVec4: w normalize is performed, w in vec3 is ignored
MAF void mat4_transform(mat4 m, vec3 v) {

View File

@ -401,20 +401,12 @@ bool os_is_key_down(os_key key) {
// Private, must be declared manually to use
struct ANativeActivity* os_get_activity() {
return state.app->activity;
void* os_get_java_vm() {
return state.app->activity->vm;
}
int os_get_activity_state() {
return state.app->activityState;
}
ANativeWindow* os_get_native_window() {
return state.app->window;
}
JNIEnv* os_get_jni() {
return state.jni;
void* os_get_jni_context() {
return state.app->activity->clazz;
}
const char** os_vk_get_instance_extensions(uint32_t* count) {

View File

@ -41,13 +41,14 @@ static EM_BOOL onFocusChanged(int type, const EmscriptenFocusEvent* data, void*
}
static EM_BOOL onResize(int type, const EmscriptenUiEvent* data, void* userdata) {
emscripten_webgl_get_drawing_buffer_size(state.context, &state.framebufferWidth, &state.framebufferHeight);
int newWidth, newHeight;
int newWidth, newHeight, newFramebufferWidth, newFramebufferHeight;
emscripten_get_canvas_element_size(CANVAS, &newWidth, &newHeight);
if (state.width != (uint32_t) newWidth || state.height != (uint32_t) newHeight) {
emscripten_webgl_get_drawing_buffer_size(state.context, &newFramebufferWidth, &newFramebufferHeight);
state.width = newWidth;
state.height = newHeight;
state.framebufferWidth = newFramebufferWidth;
state.framebufferHeight = newFramebufferHeight;
if (state.onWindowResize) {
state.onWindowResize(state.width, state.height);
return true;
@ -181,7 +182,7 @@ static EM_BOOL onKeyEvent(int type, const EmscriptenKeyboardEvent* data, void* u
return false;
}
bool os_init() {
bool os_init(void) {
emscripten_set_beforeunload_callback(NULL, onBeforeUnload);
emscripten_set_focus_callback(CANVAS, NULL, true, onFocusChanged);
emscripten_set_blur_callback(CANVAS, NULL, true, onFocusChanged);
@ -194,7 +195,7 @@ bool os_init() {
return true;
}
void os_destroy() {
void os_destroy(void) {
emscripten_set_beforeunload_callback(NULL, NULL);
emscripten_set_focus_callback(CANVAS, NULL, true, NULL);
emscripten_set_blur_callback(CANVAS, NULL, true, NULL);
@ -206,19 +207,19 @@ void os_destroy() {
emscripten_set_keyup_callback(CANVAS, NULL, true, NULL);
}
const char* os_get_name() {
const char* os_get_name(void) {
return "Web";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
return 1;
}
void os_open_console() {
void os_open_console(void) {
//
}
double os_get_time() {
double os_get_time(void) {
return emscripten_get_now() / 1000.;
}
@ -230,7 +231,7 @@ void os_request_permission(os_permission permission) {
//
}
void os_poll_events() {
void os_poll_events(void) {
//
}
@ -289,12 +290,18 @@ bool os_window_open(const os_window_config* flags) {
}
emscripten_webgl_make_context_current(state.context);
emscripten_webgl_get_drawing_buffer_size(state.context, &state.framebufferWidth, &state.framebufferHeight);
emscripten_get_canvas_element_size(CANVAS, &state.width, &state.height);
int width, height, framebufferWidth, framebufferHeight;
emscripten_webgl_get_drawing_buffer_size(state.context, &framebufferWidth, &framebufferHeight);
emscripten_get_canvas_element_size(CANVAS, &width, &height);
state.width = width;
state.height = height;
state.framebufferWidth = framebufferWidth;
state.framebufferHeight = framebufferHeight;
return true;
}
bool os_window_is_open() {
bool os_window_is_open(void) {
return state.context > 0;
}

View File

@ -28,6 +28,7 @@ typedef enum {
TYPE_BOOLEAN,
TYPE_NUMBER,
TYPE_STRING,
TYPE_MINISTRING,
TYPE_OBJECT
} VariantType;
@ -38,6 +39,10 @@ typedef union {
char* pointer;
size_t length;
} string;
struct {
uint8_t length;
char data[23];
} ministring;
struct {
void* pointer;
const char* type;

View File

@ -1,91 +0,0 @@
#include "filesystem/file.h"
#include "filesystem/filesystem.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
// Currently only read operations are supported by File and all files are read into memory fully on open
typedef struct {
uint8_t* data;
size_t offset;
size_t size;
} FileInner;
File* lovrFileInit(File* file ,const char* path) {
file->path = path;
file->handle = NULL;
file->mode = 0;
return file;
}
void lovrFileDestroy(void* ref) {
File* file = ref;
if (file->handle) {
lovrFileClose(file);
}
free(file);
}
bool lovrFileOpen(File* file, FileMode mode) {
lovrAssert(!file->handle, "File is already open");
file->mode = mode;
if (mode == OPEN_WRITE || mode == OPEN_APPEND)
return false;
FileInner *fileInner = malloc(sizeof(FileInner));
fileInner->offset = 0;
fileInner->data = lovrFilesystemRead(file->path, -1, &fileInner->size);
file->handle = fileInner;
if (!fileInner->data) {
fileInner->size = 0;
return false;
}
return true;
}
void lovrFileClose(File* file) {
lovrAssert(file->handle, "File must be open to close it");
FileInner *fileInner = (FileInner *)file->handle;
free(fileInner->data);
free(file->handle);
file->handle = NULL;
}
size_t lovrFileRead(File* file, void* data, size_t bytes) {
lovrAssert(file->handle && file->mode == OPEN_READ, "File must be open for reading");
FileInner *fileInner = (FileInner *)file->handle;
if (fileInner->offset + bytes > fileInner->size)
return 0;
memcpy(data, fileInner->data + fileInner->offset, bytes);
fileInner->offset += bytes;
return bytes;
}
size_t lovrFileWrite(File* file, const void* data, size_t bytes) {
lovrThrow("Writing not supported");
}
size_t lovrFileGetSize(File* file) {
lovrAssert(file->handle, "File must be open to get its size");
FileInner *fileInner = (FileInner *)file->handle;
return fileInner->size;
}
bool lovrFileSeek(File* file, size_t position) {
lovrAssert(file->handle, "File must be open to seek");
FileInner *fileInner = (FileInner *)file->handle;
if (position >= fileInner->size) // FIXME: Should seeking to fileInner->size exactly be allowed?
return false;
fileInner->offset = position;
return true;
}
size_t lovrFileTell(File* file) {
lovrAssert(file->handle, "File must be open to tell");
FileInner *fileInner = (FileInner *)file->handle;
return fileInner->offset;
}

View File

@ -1,26 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#pragma once
typedef enum {
OPEN_READ,
OPEN_WRITE,
OPEN_APPEND
} FileMode;
typedef struct {
const char* path;
void* handle;
FileMode mode;
} File;
File* lovrFileInit(File* file, const char* filename);
void lovrFileDestroy(void* ref);
bool lovrFileOpen(File* file, FileMode mode);
void lovrFileClose(File* file);
size_t lovrFileRead(File* file, void* data, size_t bytes);
size_t lovrFileWrite(File* file, const void* data, size_t bytes);
size_t lovrFileGetSize(File* file);
bool lovrFileSeek(File* file, size_t position);
size_t lovrFileTell(File* file);

View File

@ -433,7 +433,10 @@ const char* lovrFilesystemGetRequirePath() {
}
void lovrFilesystemSetRequirePath(const char* requirePath) {
strncpy(state.requirePath, requirePath, sizeof(state.requirePath) - 1);
size_t length = strlen(requirePath);
lovrCheck(length < sizeof(state.requirePath), "Require path is too long");
memcpy(state.requirePath, requirePath, length);
state.requirePath[length] = '\0';
}
// Archive: dir

View File

@ -497,23 +497,23 @@ bool lovrGraphicsInit(GraphicsConfig* config) {
}
gpu_slot builtinSlots[] = {
{ 0, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_ALL }, // Globals
{ 1, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_ALL }, // Cameras
{ 2, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_ALL }, // Draw data
{ 3, GPU_SLOT_SAMPLER, GPU_STAGE_ALL }, // Default sampler
{ 0, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_GRAPHICS }, // Globals
{ 1, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_GRAPHICS }, // Cameras
{ 2, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_GRAPHICS }, // Draw data
{ 3, GPU_SLOT_SAMPLER, GPU_STAGE_GRAPHICS } // Default sampler
};
state.builtinLayout = getLayout(builtinSlots, COUNTOF(builtinSlots));
gpu_slot materialSlots[] = {
{ 0, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Data
{ 1, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Color
{ 2, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Glow
{ 3, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Occlusion
{ 4, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Metalness
{ 5, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Roughness
{ 6, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT }, // Clearcoat
{ 7, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } // Normal
{ 0, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_GRAPHICS }, // Data
{ 1, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Color
{ 2, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Glow
{ 3, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Occlusion
{ 4, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Metalness
{ 5, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Roughness
{ 6, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS }, // Clearcoat
{ 7, GPU_SLOT_SAMPLED_TEXTURE, GPU_STAGE_GRAPHICS } // Normal
};
state.materialLayout = getLayout(materialSlots, COUNTOF(materialSlots));
@ -2066,6 +2066,7 @@ Material* lovrMaterialCreate(const MaterialInfo* info) {
lovrRetain(textures[i]);
Texture* texture = textures[i] ? textures[i] : state.defaultTexture;
lovrCheck(i == 0 || texture->info.type == TEXTURE_2D, "Material textures must be 2D");
lovrCheck(texture->info.usage & TEXTURE_SAMPLE, "Textures must be created with the 'sample' usage to use them in Materials");
bindings[i + 1] = (gpu_binding) { i + 1, GPU_SLOT_SAMPLED_TEXTURE, .texture = texture->gpu };
material->hasWritableTexture |= texture->info.usage != TEXTURE_SAMPLE;
}
@ -3090,6 +3091,8 @@ bool lovrReadbackWait(Readback* readback) {
return false;
}
beginFrame();
bool waited = gpu_wait_tick(readback->tick);
if (waited) {
@ -5506,7 +5509,7 @@ static void releasePassResources(void) {
lovrRelease(access->texture, lovrTextureDestroy);
}
if (pass->info.type == PASS_RENDER) {
if (pass->info.type == PASS_RENDER || pass->info.type == PASS_COMPUTE) {
for (size_t j = 0; j <= pass->pipelineIndex; j++) {
Pipeline* pipeline = pass->pipeline - j;
lovrRelease(pipeline->font, lovrFontDestroy);

View File

@ -19,8 +19,8 @@
#include <windows.h>
#elif defined(__ANDROID__)
#define XR_USE_PLATFORM_ANDROID
struct ANativeActivity* os_get_activity(void);
#include <android_native_app_glue.h>
void* os_get_java_vm(void);
void* os_get_jni_context(void);
#include <jni.h>
#endif
@ -180,6 +180,7 @@ static struct {
bool handTrackingAim;
bool handTrackingMesh;
bool controllerModel;
bool headless;
bool keyboardTracking;
bool overlay;
bool refreshRate;
@ -334,11 +335,10 @@ static bool openxr_init(HeadsetConfig* config) {
return false;
}
ANativeActivity* activity = os_get_activity();
XrLoaderInitInfoAndroidKHR loaderInfo = {
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
.applicationVM = activity->vm,
.applicationContext = activity->clazz
.applicationVM = os_get_java_vm(),
.applicationContext = os_get_jni_context()
};
if (XR_FAILED(xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*) &loaderInfo))) {
@ -367,9 +367,10 @@ static bool openxr_init(HeadsetConfig* config) {
{ "XR_FB_hand_tracking_aim", &state.features.handTrackingAim, true },
{ "XR_FB_hand_tracking_mesh", &state.features.handTrackingMesh, true },
{ "XR_MSFT_controller_model", &state.features.controllerModel, true },
{ "XR_FB_keyboard_tracking", &state.features.keyboardTracking, true },
{ "XR_MND_headless", &state.features.headless, true },
{ "XR_EXTX_overlay", &state.features.overlay, config->overlay },
{ "XR_HTCX_vive_tracker_interaction", &state.features.viveTrackers, true },
{ "XR_FB_keyboard_tracking", &state.features.keyboardTracking, true }
{ "XR_HTCX_vive_tracker_interaction", &state.features.viveTrackers, true }
};
uint32_t enabledExtensionCount = 0;
@ -827,37 +828,47 @@ static bool openxr_init(HeadsetConfig* config) {
}
static void openxr_start(void) {
{ // Session
#ifdef LOVR_VK
XrGraphicsRequirementsVulkanKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, NULL };
PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR;
XR_LOAD(xrGetVulkanGraphicsRequirements2KHR);
XR(xrGetVulkanGraphicsRequirements2KHR(state.instance, state.system, &requirements));
if (XR_VERSION_MAJOR(requirements.minApiVersionSupported) > 1 || XR_VERSION_MINOR(requirements.minApiVersionSupported) > 1) {
lovrThrow("OpenXR Vulkan version not supported");
}
uint32_t queueFamilyIndex, queueIndex;
gpu_vk_get_queue(&queueFamilyIndex, &queueIndex);
XrGraphicsBindingVulkanKHR graphicsBinding = {
.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
.instance = (VkInstance) gpu_vk_get_instance(),
.physicalDevice = (VkPhysicalDevice) gpu_vk_get_physical_device(),
.device = (VkDevice) gpu_vk_get_device(),
.queueFamilyIndex = queueFamilyIndex,
.queueIndex = queueIndex
};
#ifdef LOVR_DISABLE_GRAPHICS
bool hasGraphics = false;
#else
#error "Unsupported renderer"
bool hasGraphics = lovrGraphicsIsInitialized();
#endif
{ // Session
XrSessionCreateInfo info = {
.type = XR_TYPE_SESSION_CREATE_INFO,
.next = &graphicsBinding,
.systemId = state.system
};
#if !defined(LOVR_DISABLE_GRAPHICS) && defined(LOVR_VK)
XrGraphicsBindingVulkanKHR graphicsBinding = {
.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
.next = info.next
};
XrGraphicsRequirementsVulkanKHR requirements = {
.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR
};
if (hasGraphics) {
PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR;
XR_LOAD(xrGetVulkanGraphicsRequirements2KHR);
XR(xrGetVulkanGraphicsRequirements2KHR(state.instance, state.system, &requirements));
if (XR_VERSION_MAJOR(requirements.minApiVersionSupported) > 1 || XR_VERSION_MINOR(requirements.minApiVersionSupported) > 1) {
lovrThrow("OpenXR Vulkan version not supported");
}
graphicsBinding.instance = (VkInstance) gpu_vk_get_instance();
graphicsBinding.physicalDevice = (VkPhysicalDevice) gpu_vk_get_physical_device();
graphicsBinding.device = (VkDevice) gpu_vk_get_device();
gpu_vk_get_queue(&graphicsBinding.queueFamilyIndex, &graphicsBinding.queueIndex);
info.next = &graphicsBinding;
}
#endif
lovrAssert(hasGraphics || state.features.headless, "Graphics module is not available, and headless headset is not supported");
#ifdef XR_EXTX_overlay
XrSessionCreateInfoOverlayEXTX overlayInfo = {
.type = XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX,
@ -922,7 +933,8 @@ static void openxr_start(void) {
}
}
{ // Swapchain
// Swapchain
if (hasGraphics) {
state.depthFormat = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F;
if (state.config.stencil && !lovrGraphicsIsFormatSupported(state.depthFormat, TEXTURE_FEATURE_RENDER)) {