mirror of https://github.com/bjornbytes/lovr.git
Merge branch 'dev' into MSFT_controller_model
This commit is contained in:
commit
78a70670bb
|
@ -0,0 +1 @@
|
|||
github: bjornbytes
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 29c0457cc167bfc9e9361a3818440e388986f5b5
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
103
src/api/api.h
103
src/api/api.h
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#include "gpu.h"
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
bool gpu_init(gpu_config* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void gpu_destroy(void) {
|
||||
//
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Reference in New Issue