Compare commits

...

192 Commits

Author SHA1 Message Date
bjorn bdeee99953 Tracy; 2024-04-10 14:06:57 -07:00
bjorn aef71192ac Add ConvexShape;
Also luax_readmesh supports ModelData input, and the indices pointer can
be NULL to only request the vertex positions.
2024-04-09 13:01:30 -07:00
bjorn f1ba4d1a1e Clean up Joint lists further; 2024-04-08 11:11:28 -07:00
bjorn 5df8405af3 Cleanup; Methods take vectors;
Ugh this was way worse than I thought
2024-04-07 20:27:10 -07:00
bjorn 2952ed764d Fix issue with Collider:getJoints;
This can probably be simplified somehow...
2024-04-07 15:22:06 -07:00
bjorn 985ba6bd13 Fix collider leak; 2024-04-07 15:13:33 -07:00
bjorn 15649802c8 Rework World Collider/Joint lists;
- Add World:getColliderCount
- Add World:getJointCount
- Add World:getJoints
- Add Joint:isDestroyed
- Destroy joints when either of their colliders are destroyed (Jolt
  seems to require this).
- Fix joint memory leak.
- Change joints to be stored in some crazy graph thing.
  - Reduces allocations, allows constant time insertion/removal.
2024-04-07 15:08:22 -07:00
Bjorn e3b42fe24b
Merge pull request #762 from bjornbytes/compound-shape
CompoundShape
2024-04-07 14:34:28 -07:00
bjorn 060e72c525 Rename CompoundShape 'shape' to 'child';
Maybe slightly less ambiguous/confusing?
2024-04-07 13:47:52 -07:00
bjorn 47dec01772 rm more useless checks; 2024-04-07 13:47:52 -07:00
bjorn 582ee1625f CompoundShape:replaceShape fixes; 2024-04-07 13:47:52 -07:00
bjorn e36374c4cf rm useless check; 2024-04-07 13:47:52 -07:00
bjorn 98601b3425 Collider:setShape accepts nil;
Internally it still uses a teeny tiny sphere (it's kinda like the
collider's shape is a "point").
2024-04-07 13:47:52 -07:00
bjorn 2ac0d6946e Move shape arg to last argument of query callbacks;
Also fix an off-by-one error.
2024-04-07 13:47:52 -07:00
bjorn b589cec975 Collider:getShape takes optional child index;
For convenience when you're trying to dig a leaf shape out of a CompoundShape.

It's ignored if the collider's shape is not a CompoundShape.
2024-04-07 13:47:52 -07:00
bjorn 8bdab9f2d4 Shape:getAABB; 2024-04-07 13:47:52 -07:00
bjorn 5751068728 Move Shape:is/setSensor and Shape:is/setEnabled to Collider; 2024-04-07 13:47:52 -07:00
bjorn 481a9dafa2 newCollider takes optional Shape as first arg; 2024-04-07 13:47:52 -07:00
bjorn 95cc6c2753 Shape fixes;
- Collider always has Shape now.  When left off or set to null, it's a
  tiny sphere (representing a point).
- Preserve shape offset when changing shape.
2024-04-07 13:47:52 -07:00
bjorn f35cda7db8 lovrColliderCreate takes shape;
Jolt requires a shape, also it saves a bit of work.
2024-04-07 13:47:52 -07:00
bjorn 907d89c913 Add Collider:getShapes for backcompat; 2024-04-07 13:47:52 -07:00
bjorn ff0f39fc7b rm unused shape prototypes; 2024-04-07 13:47:52 -07:00
bjorn b809db1d83 ODE compatibility; 2024-04-07 13:47:52 -07:00
bjorn 051266f8ea Collider shape is required I think;
Jolt doesn't really support bodies without shapes.
2024-04-07 13:47:52 -07:00
bjorn 930e713079 Add Collider:get/setShapeOffset; 2024-04-07 13:47:52 -07:00
bjorn 4f26182595 World queries return collider/shapeindex instead of Shape; 2024-04-07 13:47:52 -07:00
bjorn 8cbdda00f3 Require frozen CompoundShapes to have at least 2 children;
If you create a StaticCompoundShape with 1 child, Jolt creates a
RotatedTranslatedShape.  It would be a pain to have to branch on that,
so let's just require 2 children.
2024-04-07 13:47:52 -07:00
bjorn 10f965d69e rm Shape setters;
Jolt doesn't support this and requires you to recreate the shape.  Since
the shape -> collider relationship is 1:N, we can't really create a new
shape because we'd have to figure out which colliders/compoundshapes to
assign it to, which isn't feasible.
2024-04-07 13:47:52 -07:00
bjorn a7bf4ca2a4 CompoundShape fixes; 2024-04-07 13:47:52 -07:00
bjorn c27694fd8e Shapes set their userdata; 2024-04-07 13:47:52 -07:00
bjorn b3e9e55b8a CompoundShape API; 2024-04-07 13:47:52 -07:00
bjorn 6f0b6391df Start CompoundShape; 2024-04-07 13:47:52 -07:00
bjorn ce58619094 Colliders can only have 1 shape; rm shape pose; 2024-04-07 13:47:52 -07:00
bjorn 9b6f58ac1e Update Jolt; 2024-04-07 13:47:52 -07:00
bjorn f4d1161a3d Disable Jolt profiler and debug renderer; 2024-04-07 13:46:57 -07:00
bjorn f4d5c0d758 Add assert for body count; 2024-04-06 00:47:31 -07:00
bjorn 38a68dc4e5 TerrainShape image must be square; 2024-04-06 00:12:50 -07:00
bjorn 5e521b7698 Collider:applyLinear/AngularImpulse; 2024-04-05 19:16:39 -07:00
bjorn fb6b402034 Add Collider:is/setContinuous; 2024-04-05 19:05:25 -07:00
bjorn e9fe088521 Add Collider:get/setGravityScale; Deprecate Collider:get/setGravityIgnored; 2024-04-05 14:22:02 -07:00
bjorn 01194a80c7 Deprecate several World methods;
They still work on ODE, and Jolt makes a best-effort attempt to support
them, but they will be removed in a future version.

- Damping should be set explicitly on colliders.
- Tightness and response time will be supported via spring forces, and
  also some global settings in newWorld will affect joints too.
- Step count will be added to :update soon.  Since the correct value is
  dt-dependent in Jolt, a persistent accessor doesn't make sense.
- Sleeping allowed will be an immutable setting in newWorld soon, but
  otherwise should be changed per-collider.
2024-04-05 14:07:56 -07:00
bjorn ffc23497a9 Details; 2024-04-05 13:58:28 -07:00
bjorn d756cc2b1d Add variant of newWorld that takes an options table; 2024-04-05 13:25:32 -07:00
bjorn 77abad4e21 Merge branch 'stable' into dev 2024-04-05 11:42:18 -07:00
bjorn 40879f383a Fix possible crash when using math module in multiple threads; 2024-04-05 11:41:51 -07:00
bjorn 8596bb06a0 Fix OBJ triangulation for faces with more than 4 vertices; 2024-04-04 17:34:04 -07:00
bjorn 3dd641c02d Undo accidental jolt submodule bump; 2024-04-04 17:31:42 -07:00
bjorn 01a0df37cc Fix OBJ triangulation for faces with more than 4 vertices; 2024-04-04 17:28:05 -07:00
bjorn ecbd331552 Details; 2024-04-01 20:49:50 -07:00
bjorn fdba60214c Fix Collider/Shape/Joint UserData leak;
refs are cumbersome because they require storing an int and they require
manual cleanup when the object is destroyed.

Instead, we'll go with a pure Lua solution using a table in the
registry.  The table has weak keys containing the LÖVR object, and the
values are the userdata.  This doesn't require any manual cleanup,
reduces the size of objects by 8 bytes, and is about 25% faster.
2024-04-01 16:03:42 -07:00
bjorn 243b6ae21a Use lovrDefer more; 2024-04-01 15:32:06 -07:00
bjorn 1c070990de Add support for Lua 5.4 <close> variables;
Taken from LÖVE.
2024-03-30 09:53:01 -07:00
bjorn 458e7b176c rm job system error handling;
Having it in the job system is too gross.  Use lovrTry instead.
2024-03-29 18:11:21 -07:00
bjorn cdecc1bae7 Add defer system; Rework exceptions; util cleanup;
Defer can be used to clean up resources when an error occurs.
2024-03-29 18:10:22 -07:00
bjorn 67e228125b Add d24 TextureFormat;
LÖVR uses d32f by default, but that's not guaranteed to be supported.

However, GPUs must support either d32f or d24, so we can fall back to
d24 reliably.
2024-03-29 18:07:25 -07:00
bjorn c37cd3852f tup: add -Wno-strict-prototypes for now;
glslang and ODE are the troublemakers.  I updated glslang but I'm too
lazy to update ODE, so I'm just gonna wait until we switch to jolt.
2024-03-29 18:04:42 -07:00
bjorn b7e1b5111e jolt: fix some shape memory leaks; 2024-03-29 15:26:10 -07:00
bjorn c5d6bb6b48 jolt: rm matrix copies;
JoltC now uses floats for its RVec/RMatrix structs in single precision
mode, so we can operate on them as mat4's directly, copy/cast them, etc.
2024-03-29 15:26:03 -07:00
bjorn 87cd13d95c jolt: adjust includes; 2024-03-29 15:25:41 -07:00
bjorn 822eb94993 CMake: rm physics library log messages; 2024-03-29 14:54:35 -07:00
bjorn cfd2374449 Update Jolt; 2024-03-29 14:50:00 -07:00
bjorn 55f0da97ce Update glslang; 2024-03-28 21:00:24 -07:00
bjorn e7d0764e49 Update Jolt;
Also disable the HelloWorldNative sample...
2024-03-28 15:28:40 -07:00
bjorn f4553075cb Upgrade Jolt;
Seems we can add the root CMake project directly now.
2024-03-28 12:58:33 -07:00
bjorn fd7fe08055 Upgrade lovr-http; 2024-03-28 12:47:35 -07:00
bjorn 8ec7a18c28 Update lua-enet; 2024-03-28 12:46:20 -07:00
bjorn 7373bb6b83 Bump minimum CMake version;
Newer versions of CMake show a deprecation warning that < 3.5 will be
unsupported soon.
2024-03-28 12:42:22 -07:00
bjorn abd2eddf0d Fix some strict prototype warnings; 2024-03-23 13:12:26 -07:00
bjorn 70e4b9fd7b actions: Adjust MoltenVK paths for newer SDK version;
The MoltenVK folder was removed from the SDK in favor of the macOS folder.
2024-03-17 21:37:48 -07:00
bjorn 8fc5d4fd24 actions: Update macOS Vulkan SDK version;
This version includes VK_KHR_synchronization2, which LÖVR uses now.
2024-03-17 20:42:25 -07:00
bjorn ef8d1abf87 Update changelog; 2024-03-17 14:02:56 -07:00
bjorn cc4567652e Merge branch 'stable' into dev 2024-03-17 13:59:31 -07:00
bjorn d50f661ce6 Fix possible negative dt when restarting simulator; 2024-03-17 13:59:13 -07:00
bjorn 26d17fc402 boot.lua: exit successfully when printing help; 2024-03-12 13:32:26 -07:00
Bjorn 27c59de5dc
Merge pull request #759 from brainrom/patch-2
Fix boot.lua merge
2024-03-12 13:21:39 -07:00
Ilya 994f623c5d
Fix boot.lua merge
boot.lua was broken during merging with stable branch.
2024-03-12 23:12:29 +03:00
bjorn 9c2c4f6906 Update changelog; 2024-03-12 12:02:17 -07:00
bjorn 2b218d09d0 Merge branch 'stable' into dev 2024-03-12 11:58:34 -07:00
bjorn 66f6ee9bb9 v0.17.1; 2024-03-12 11:56:30 -07:00
bjorn 38f05b9f01 Update OpenXR to 1.0.34;
(cherry picked from commit 48e484ed8a)
2024-03-12 11:06:20 -07:00
Josip Miskovic a84385a030 Fix Model:getMesh()
(cherry picked from commit 37fdb39e73)
2024-03-12 11:05:22 -07:00
bjorn c17abe4f24 CMake: disable BUILD_WITH_WAYLAND_HEADERS for OpenXR;
(cherry picked from commit 78586c3770)
2024-03-12 11:04:42 -07:00
bjorn 053893f3f6 Bump OpenXR to 1.0.33;
(cherry picked from commit da5a6cec10)
2024-03-12 11:04:38 -07:00
bjorn b09d612e6b Validate that intra-buffer copy has non-overlapping ranges;
(cherry picked from commit 06bfbbaa6b)
2024-03-12 11:04:14 -07:00
bjorn 561b20699a Fix error when loading a Model without any vertices;
(cherry picked from commit 3712bc3fba)
2024-03-12 11:03:33 -07:00
bjorn aff97b7a30 glTF: support glb models without BIN chunk;
(cherry picked from commit 35f97f08f3)
2024-03-12 11:02:21 -07:00
bjorn 1f6904c04b Fix errhand loop when debug library is unavailable;
(cherry picked from commit dacc17e1b7)
2024-03-12 11:02:00 -07:00
Josip Miskovic 8c3c0f8136 Bound check the number of sphere segments
(cherry picked from commit 3db04db05a)
2024-03-12 11:00:42 -07:00
bjorn 1e02de6cda Fix depth write not working when depth test is disabled;
A classic

(cherry picked from commit 5c46d6169a)
2024-03-12 10:55:21 -07:00
bjorn 24c13e88d3 Fix render pass cache when compiling pipelines;
Comparing the lower 32 bits to the full hash was producing false
negatives and causing unnecessary render pass creation when creating
pipelines.

(cherry picked from commit 83f106b89f)
2024-03-12 10:55:16 -07:00
bjorn 5611a6130b Fix cubemap orientation;
+Z is the front face in a cubemap, not -Z.  Currently cubemap faces are
flipped in both the X and Z directions.

Some kind of flip is required because cubemaps use a left-handed
coordinate space instead of lovr's/vulkan's right-handed coordinate
space.

Equirect does not need any changes.

(cherry picked from commit 1b1bc182bf)
2024-03-12 10:53:28 -07:00
bjorn 35228f1838 Create files/directories with more permissions;
Files are created with 664 (though usually modified by umask).

Directories are created with 755 permissions.

Main motivation is to allow adb to access files on Android 12+

(cherry picked from commit 9445331df8)
2024-03-12 10:46:14 -07:00
Josip Miskovic 0e9171e2dd Use virtual memory in vector pools
(cherry picked from commit a1d7bf5fbc)
2024-03-12 10:44:43 -07:00
bjorn ae1edd95e7 Skip creating texture views for transfer-only textures;
Vulkan forbids it!

(cherry picked from commit eac68d2fe4)
2024-03-12 10:41:20 -07:00
bjorn d097d9819d Add wrappers for malloc functions; 2024-03-11 14:38:00 -07:00
bjorn 2d7b636a90 Change more asserts to checks; 2024-03-11 00:58:21 -07:00
bjorn 0d8d6f579b rm unused lovrImageClear prototype; 2024-03-11 00:57:59 -07:00
bjorn aa79822edb Fix VertexIndex;
Adding an offset to VertexIndex/BaseVertex breaks those builtins for
shader code.  Instead, go back to binding the vertex buffer at an
appropriate offset.

This means we rebind the vertex buffer and makes it harder to batch
draws.  In the future we'd like to go back to binding all vertex buffers
at zero and incorporating per-draw vertex offsets, but we'll need to
adjust the VertexIndex macro to subtract that offset.
2024-03-10 10:36:13 -07:00
bjorn 1cda2d59a7 gpu: increase max number of memory blocks to 1024;
We are allocating buffers in smaller chunks now (4MB), and only 256 of
those would limit GPU memory usage to 1GB (in reality the cap is much
higher since static buffers and textures are still suballocated, and any
allocation bigger than 4MB will use its own block).  Still, in some
degenerate cases with huge amounts of temporary buffer memory, we are
hitting this limit earlier than we'd like.
2024-03-08 12:03:55 -08:00
bjorn f93fb0c0c7 Set buffer tick properly when destroying pass;
This was causing one of the pass's buffers to be unrecyclable.
2024-03-08 11:56:23 -08:00
bjorn 4719dbdd10 Fix crash with timestamp readback; 2024-03-07 10:26:35 -08:00
bjorn 9068c8daee rm map_remove;
It's no longer used.
2024-03-07 10:13:00 -08:00
bjorn f9cfc4e76e Update changelog; 2024-03-05 09:14:16 -08:00
bjorn dc643ad166 Shader:getBufferFormat improvements;
- Returns nil instead of erroring if variable does not exist.
- For runtime-sized arrays, returns nil for length instead of 4 billion.
2024-03-05 09:01:01 -08:00
Bjorn 600a8c372e
Merge pull request #755 from jmiskovic/fix/jolt_win
Allow joltc fns without explicit argument list
2024-03-04 11:54:10 -08:00
Josip Miskovic 8a5b88e851 Allow joltc fns without explicit argument list
The C4255 warning in MSVC complains about function lacking explicit list
of arguments (`f()` instead of `f(void)`). Disabling this warning allows
joltc to build on windows platform.
2024-03-04 19:03:22 +01:00
Bjorn b3b523cdbf
Merge pull request #754 from brainrom/patch-2
Make lovrAssert act like a function
2024-03-03 03:54:13 -08:00
Ilya 4139f0f180
Make lovrAssert act like a function
`do {} while(0)` is the perfect way to make function-like macro act like a real function (require ;, encapsulate local variables)
2024-03-03 14:37:14 +03:00
bjorn 1ec0a41bb8 Also avoid allocating empty uniform buffer for indirect/compute; 2024-03-01 17:14:08 -08:00
bjorn c3d81debe8 Clean up GPU submission; 2024-03-01 17:13:06 -08:00
bjorn 80b596daf6 job: return NULL if queue is full when starting job;
The current implementation might spin forever if only one thread is
starting/waiting on jobs, since nothing will be waiting on the jobs
(i.e. freeing up queue slots) while spinning.

The correct thing to do is to return NULL and let the caller handle
backpressure.  It's a bit more annoying to program since you might not
get a job back and need to handle it, but it's the right thing to do.
2024-03-01 16:48:28 -08:00
bjorn 79e28eb7e3 Fix issue with shader uniforms;
Shader == NULL wasn't clearing the dirty uniform flag, so if the
uniforms were dirty when you set a default shader we would try to
allocate and bind a zero-size uniform buffer.

Instead, let's avoid making a new uniform buffer if the shader doesn't
have any uniforms!
2024-03-01 12:46:08 -08:00
Bjorn fd331090c9
Merge pull request #751 from brainrom/patch-1
Fixed nogame.zip creatrion
2024-02-29 10:36:34 -08:00
Ilya f77a8f6cb2
Fixed nogame.zip creatrion
logo.spv wasn't packed in nogame.zip, therefore nogame is failed to start. Now all files in nogame folder are packed.
2024-02-29 11:35:55 +03:00
Bjorn 6c7ca1e275
Merge pull request #750 from bjornbytes/job
Job System
2024-02-28 12:51:25 -08:00
bjorn 224b6a495d Simplify job error string memory stuff;
Comparison against string literal isn't good.  I wish there was a way to
not heap allocate so much though, but exceptions are exceptional anyway.
2024-02-28 12:31:34 -08:00
bjorn 901dd268f2 job copies errors; add variadic version of job_abort;
I caved and depended on malloc/sprintf because I couldn't find anywhere
else good to put it...
2024-02-28 12:31:34 -08:00
bjorn f6af859984 job system error handling;
- Jobs can now throw exceptions using job_abort.  This will store an
  error message on the job and immediately stop the job.  The method
  will never return.  It must be called from the worker thread running
  the job, so in practice it should only be called by the job function.
- After waiting for the job to complete or abort using job_wait, the
  error message can be checked using job_get_error.  This is the raw
  pointer passed to job_abort, caller is responsible for freeing it or
  doing whatever necessary cleanup is required.
- job_free must be used to recycle the job after waiting on it.

Yes it uses longjmp.

There's also some general cleanup, and job_start just does random jobs
when the queue is full instead of returning NULL.
2024-02-28 12:31:34 -08:00
bjorn b32ac1c5a5 Details; 2024-02-28 12:31:34 -08:00
bjorn 1701409763 threads polyfill defines thread_local;
Might need to be removed for C23.
2024-02-28 12:31:34 -08:00
bjorn a4c1d9fce6 Thread module initializes job system;
Worker count can be set from conf.lua.

A negative worker count is relative to the number of cores.

-1 is the default.
2024-02-28 12:31:34 -08:00
bjorn d017ca0fac Details; 2024-02-28 12:31:34 -08:00
bjorn 35b5693a19 Add some more windows atomics; 2024-02-28 12:31:34 -08:00
bjorn e8dc1484d3 Cleanup; 2024-02-28 12:31:34 -08:00
bjorn 98e2e94ea8 Job done counter uses atomic loads/stores; 2024-02-28 12:31:34 -08:00
bjorn 4a175c4cf8 Fix atomic intrinsics; 2024-02-28 12:31:34 -08:00
bjorn 6347114030 Job system; 2024-02-28 12:31:34 -08:00
Bjorn 73eae189ae
Merge pull request #749 from bjornbytes/shader-rework
Shader Rework
2024-02-26 15:35:39 -08:00
bjorn 792fd75204 Texture view improvements;
- You can nest texture views (make a view of a view).
- You can do transfers on views (copies, clears, blits, etc.).
- You can give labels to views.
- API changes
  - Rename Texture:newView to lovr.graphics.newTextureView.
  - Constructor takes a table of options (order was too confusing).
  - Remove Texture:isView/getParent, since views can be used for everything now.
  - Better matches what LÖVE ended up doing.
- Fix bug where :getType on a view returned parent type.
2024-02-26 15:24:09 -08:00
bjorn ec380e0cfd rm pass uniformSize;
It's no longer necessary.
2024-02-25 14:58:11 -08:00
bjorn fa8ea6732b rm pass binding mask;
It's no longer necessary.
2024-02-25 14:57:27 -08:00
bjorn 466a052ded Cleanup; 2024-02-24 15:45:30 -08:00
bjorn 2fe5ba8f3b Ensure all binding fields are fully initialized; 2024-02-24 15:45:10 -08:00
bjorn ae19b7aad3 Fix resource binding numbers; 2024-02-24 15:33:09 -08:00
bjorn 652a074677 Update resource invalidation when switching shaders;
Previously, when switching shaders, resource bindings would be preserved
for resources with matching slots/types.  This doesn't make sense in a
world where binding numbers are internal.  Instead, match up resources
by name/type.

Additionally, rewire all the uniforms by name/size so uniforms with the
same name get preserved (name/type would be too hard for e.g. structs).
This seems like it would be horribly slow and may need to be optimized,
optional, or removed.

I didn't test any of this lol, but I will I promise.
2024-02-24 14:34:29 -08:00
bjorn bd83ad6eb4 Cleanup; 2024-02-24 11:49:11 -08:00
bjorn 48e484ed8a Update OpenXR to 1.0.34; 2024-02-24 11:05:39 -08:00
Bjorn f3cae5f80e
Merge pull request #748 from bjornbytes/rm-msaa-textures
Remove Multisample Textures
2024-02-23 12:38:23 -08:00
Bjorn bad3d8b78a
Merge pull request #747 from bjornbytes/changelog
Changelog
2024-02-23 12:38:12 -08:00
Bjorn f1158b2159
Merge pull request #745 from bjornbytes/xr-swapchain-colorspace
Fix OpenXR Swapchain Colorspaces
2024-02-23 12:37:54 -08:00
bjorn 114109ab54 rm multisample texture objects; 2024-02-22 14:40:32 -08:00
bjorn b65fa0d1f6 Add changelog; 2024-02-22 14:16:54 -08:00
bjorn b63ec6e137 Graphics shader requires fragment stage for now;
The vertex-only shader requires too much juggling of shader stage flags
and pipeline layout compatibility when e.g. binding push constants and
descriptor sets.
2024-02-21 10:37:59 -08:00
bjorn 6a669bd967 rm var shader macro;
It is no longer necessary, and wasn't widely used/documented.
2024-02-21 10:28:30 -08:00
bjorn ada33c6620 Downgrade glslang to 13.0.0 with lovr cherrypicks; 2024-02-21 10:25:00 -08:00
bjorn cbc9312716 Fix memory leak; 2024-02-21 10:21:42 -08:00
bjorn 728b166d69 Fix binding numbers; 2024-02-21 10:21:42 -08:00
bjorn 64e253a8ef mv logo shader to nogame bundle; 2024-02-21 10:21:42 -08:00
bjorn 81ef58d032 Use push constant instead of BaseInstance for DrawID;
This is a little slower, but means indirect draws can use Transform and Color.

There are other solutions for this.  For example LÖVR could reserve
BaseInstance and use a compute shader to rewrite indirect buffers.

For now we choose to be correct and a little slower.
2024-02-21 10:21:42 -08:00
bjorn 875a7f8237 Shader rework;
- Undeprecate ShaderType (it's good actually, kinda like a pipeline bind
  point and more specific than having lovr trying to make sense of a
  random set of stages).
- Use a default uniform block instead of push constants.  The Constants
  macro now maps to a uniform block declaration.
- It is no longer possible to create a shader by giving a single
  DefaultShader string (you can still give a per-stage DefaultShader
  though).
- lovr.graphics.compileShader now takes all stages instead of a single
  stage.  In general we need all stages when compiling GLSL because
  default uniforms require linking across stages.
2024-02-21 10:21:41 -08:00
bjorn 8b78a4d3b5 core/gpu: depth-only passes can include fragment shader; 2024-02-20 10:19:16 -08:00
bjorn d4e2736e0b Update glslang;
Includes a fix for generating SPIRV for multiple shader stages.

Also reverts a buggy upstream commit temporarily.
2024-02-20 10:17:20 -08:00
bjorn 45eba2fe85 rm ability to send resources by slot number; 2024-02-20 10:16:41 -08:00
bjorn 93348499e9 Binding numbers are assigned explicitly and merged across stages; 2024-02-20 10:16:41 -08:00
bjorn 035d359133 Add relaxed Vulkan flag; 2024-02-20 10:16:41 -08:00
bjorn f7c1d4cccb core/spv: return pointer to set/binding decorations instead of value; 2024-02-20 10:16:41 -08:00
bjorn eecd201f57 tup: use version-qualified library name for LuaJIT; 2024-02-20 09:35:56 -08:00
bjorn c6f010a572 tup: fix build; support toggling between jolt/ode; 2024-02-20 09:35:56 -08:00
Bjorn 10b0c81209
Merge pull request #746 from jmiskovic/fix/modelGetMesh
Fix Model:getMesh()
2024-02-19 15:01:27 -08:00
Josip Miskovic 37fdb39e73 Fix Model:getMesh() 2024-02-19 23:49:18 +01:00
bjorn 555d97afbe core/spv: parse texture properties;
Can detect if texture is cubemap, array, 2d, multisample, etc.
2024-02-18 20:36:33 -08:00
Bjorn f2bcfee8be
Merge pull request #735 from jmiskovic/jolt-rebase
Jolt physics engine integration
2024-02-17 12:11:41 -08:00
bjorn 0bf09ca108 Rearrange the way sRGB textures work (again); 2024-02-16 12:45:35 -08:00
Bjorn 2770ab270a
Merge pull request #743 from jmiskovic/simulator-supersampling
Headset simulator supersampling
2024-02-14 10:50:39 -08:00
Josip Miskovic 7229a93f11 Headset simulator supersampling 2024-02-14 19:34:13 +01:00
Josip Miskovic 47c9b20542 Jolt world-level damping and sleeping allowed
The damping 0.05 value comes from Jolt's BodyCreationSettings.h.
2024-02-13 19:18:57 +01:00
Josip Miskovic 0b8546fd62 Jolt MSVC workaround; set ODE as default engine 2024-02-13 17:34:18 +01:00
Josip Miskovic 431062c345 Increase Jolt body & constraint limits
The new values come from comments in the official HelloWorld.cpp. After
the changes the any increase of memory consumption is within the
measurement error.
2024-02-13 17:25:43 +01:00
bjorn 41deb706ce rm steamvr workaround for local-floor reference space; 2024-02-09 21:17:11 -08:00
bjorn 7b39a30600 Fix sType field in gpu_xr_acquire; 2024-02-09 19:01:55 -08:00
bjorn 78586c3770 CMake: disable BUILD_WITH_WAYLAND_HEADERS for OpenXR; 2024-02-09 11:01:06 -08:00
bjorn da5a6cec10 Bump OpenXR to 1.0.33; 2024-02-09 11:00:59 -08:00
bjorn bf97a55fcb Fix issues with Pass buffer recycling;
- When destroyed, Pass should only recycle its buffer if it actually has one.
- When resetting, chain all of the buffers at once instead of one-by-one (N^2)
2024-02-08 11:32:01 -08:00
bjorn df3544e5f3 Fix issue with buffer recycling;
Must set next pointer to NULL!
2024-02-08 11:22:34 -08:00
Bjorn d23164235b
Merge pull request #732 from bjornbytes/model-vertex-compression
Model Vertex Compression
2024-01-31 13:35:58 -08:00
bjorn 215e042e05 Add lovrUnreachable branch; 2024-01-31 12:52:29 -08:00
Josip Miskovic 1ad8f23fb8 Jolt physics engine integration 2024-01-30 18:22:27 +01:00
bjorn 06bfbbaa6b Validate that intra-buffer copy has non-overlapping ranges; 2024-01-29 21:33:51 -08:00
bjorn 5bd48f16f5 lovr.system.get/setClipboardText; 2024-01-29 12:52:41 -08:00
bjorn 287769f1f2 Fix 32 bit sync flag truncation; Fix sync validation; 2024-01-29 02:10:40 -08:00
bjorn 6516cca39d Switch to VK_KHR_synchronization2; 2024-01-29 00:58:11 -08:00
bjorn 3712bc3fba Fix error when loading a Model without any vertices; 2024-01-29 00:33:34 -08:00
bjorn 35f97f08f3 glTF: support glb models without BIN chunk; 2024-01-29 00:29:23 -08:00
bjorn 2b351fa145 Clean up NodeTransform struct a bit; 2024-01-26 10:47:54 -08:00
bjorn 1ede3ef012 gpu_wait_idle also updates tick;
So you don't have to call `gpu_wait_tick` right after.
2024-01-25 00:13:25 -08:00
bjorn dacc17e1b7 Fix errhand loop when debug library is unavailable; 2024-01-24 17:54:07 -08:00
bjorn f8158744f9 Details; 2024-01-20 21:58:03 -08:00
Bjorn 30cb0c19a0
Merge pull request #733 from xiejiangzhi/slow_move_32_tag_and_other_fix
Slow movement for sim, fix bug of tag, add max number of tag to 32
2024-01-20 21:56:38 -08:00
xiejiangzhi 45a6333afb Slow movement for sim, fix bug of tag, add max number of tag to 32
Holding ctrl to slowly movement for Sim
Check null for findTag
Increment max number of tags to 32
Fix newWorld error if tags > MAX_TAGS
2024-01-21 13:50:59 +08:00
bjorn a90c4092f8 glslang: Add -gVS when compiling default shaders for debug build;
This allows RenderDoc to debug the default shaders.
2024-01-20 21:47:27 -08:00
bjorn 40d85c6a16 Model uses sn10x3 normals and tangents;
This shaves 20 bytes off of each model vertex, or around 40% savings.
The vertex size is also a power of two which results in extreme amounts
of style points.
2024-01-20 17:39:36 -08:00
bjorn 5434dd6add Add sn10x3 data type;
The unpacking code might not be working properly...
2024-01-20 17:39:36 -08:00
89 changed files with 6329 additions and 2711 deletions

View File

@ -107,7 +107,7 @@ jobs:
steps:
- name: Install Wulkan
env:
VKV: 1.3.231.1
VKV: 1.3.275.0
run: |
curl -sOL https://sdk.lunarg.com/sdk/download/$VKV/mac/vulkansdk-macos-$VKV.dmg?Human=true
hdiutil attach vulkansdk-macos-$VKV.dmg
@ -125,11 +125,11 @@ jobs:
run: cmake --build build
- name: Package
run: >
cp $VULKAN_SDK/lib/libvulkan.1.3.231.dylib build/lovr.app/Contents/MacOS/libvulkan.1.dylib &&
cp $VULKAN_SDK/../MoltenVK/dylib/macOS/libMoltenVK.dylib build/lovr.app/Contents/MacOS &&
cp $VULKAN_SDK/lib/libvulkan.1.3.275.dylib build/lovr.app/Contents/MacOS/libvulkan.1.dylib &&
cp $VULKAN_SDK/lib/libMoltenVK.dylib build/lovr.app/Contents/MacOS &&
mkdir -p build/lovr.app/Contents/Resources/vulkan/icd.d &&
cp $VULKAN_SDK/../MoltenVK/dylib/macOS/MoltenVK_icd.json build/lovr.app/Contents/Resources/vulkan/icd.d &&
sed -i '' 's|./libMoltenVK.dylib|../../../MacOS/libMoltenVK.dylib|' build/lovr.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json &&
cp $VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json build/lovr.app/Contents/Resources/vulkan/icd.d &&
sed -i '' 's|../../../lib/libMoltenVK.dylib|../../../MacOS/libMoltenVK.dylib|' build/lovr.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json &&
ditto -c -k --keepParent build/lovr.app lovr.zip
- name: Upload
uses: actions/upload-artifact@v3

6
.gitmodules vendored
View File

@ -31,3 +31,9 @@
[submodule "plugins/lovr-http"]
path = plugins/lovr-http
url = https://github.com/bjornbytes/lovr-http
[submodule "deps/jolt-physics-sharp"]
path = deps/jolt-physics-sharp
url = https://github.com/amerkoleci/JoltPhysicsSharp
[submodule "deps/tracy"]
path = deps/tracy
url = https://github.com/wolfpld/tracy

1232
CHANGES.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.3.0)
cmake_minimum_required(VERSION 3.5.0)
cmake_policy(SET CMP0063 NEW)
cmake_policy(SET CMP0079 NEW)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS deployment version")
@ -29,7 +29,9 @@ option(LOVR_USE_WEBXR "Enable the WebXR backend for the headset module" OFF)
option(LOVR_USE_SIMULATOR "Enable the keyboard/mouse backend for the headset module" ON)
option(LOVR_USE_STEAM_AUDIO "Enable the Steam Audio spatializer (be sure to also set LOVR_STEAM_AUDIO_PATH)" OFF)
option(LOVR_USE_OCULUS_AUDIO "Enable the Oculus Audio spatializer (be sure to also set LOVR_OCULUS_AUDIO_PATH)" OFF)
option(LOVR_SANITIZE "Enable Address Sanitizer" OFF)
option(LOVR_PROFILE "Enable Tracy integration" OFF)
option(LOVR_SYSTEM_GLFW "Use the system-provided glfw" OFF)
option(LOVR_SYSTEM_LUA "Use the system-provided Lua" OFF)
@ -41,6 +43,9 @@ option(LOVR_BUILD_SHARED "Build a shared library (takes precedence over LOVR_BUI
option(LOVR_BUILD_BUNDLE "On macOS, build a .app bundle instead of a raw program" OFF)
option(LOVR_BUILD_WITH_SYMBOLS "Build with C function symbols exposed" OFF)
set(LOVR_PHYSICS_LIBRARY "ODE" CACHE STRING "Physics library to use")
set_property(CACHE LOVR_PHYSICS_LIBRARY PROPERTY STRINGS ODE JOLT)
# Setup
if(EMSCRIPTEN)
string(CONCAT EMSCRIPTEN_LINKER_FLAGS
@ -158,27 +163,34 @@ endif()
# ODE
if(LOVR_ENABLE_PHYSICS)
if(LOVR_SYSTEM_ODE)
pkg_search_module(ODE REQUIRED ode)
pkg_search_module(CCD REQUIRED ccd)
include_directories(${ODE_INCLUDE_DIRS} ${CCD_INCLUDE_DIRS})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
set(LOVR_ODE ode ccd)
else()
if(EMSCRIPTEN)
set(ODE_BUILD_SHARED OFF CACHE BOOL "")
if(LOVR_PHYSICS_LIBRARY STREQUAL "ODE")
if(LOVR_SYSTEM_ODE)
pkg_search_module(ODE REQUIRED ode)
pkg_search_module(CCD REQUIRED ccd)
include_directories(${ODE_INCLUDE_DIRS} ${CCD_INCLUDE_DIRS})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
set(LOVR_PHYSICS_LIB ode ccd)
else()
set(ODE_BUILD_SHARED ON CACHE BOOL "")
if(EMSCRIPTEN)
set(ODE_BUILD_SHARED OFF CACHE BOOL "")
else()
set(ODE_BUILD_SHARED ON CACHE BOOL "")
endif()
add_subdirectory(deps/ode ode)
if(MSVC)
set_target_properties(ode PROPERTIES COMPILE_FLAGS "/wd4244 /wd4267")
target_compile_definitions(ode PRIVATE _CRT_SECURE_NO_WARNINGS)
else()
set_target_properties(ode PROPERTIES COMPILE_FLAGS "-Wno-unused-volatile-lvalue -Wno-array-bounds -Wno-undefined-var-template")
endif()
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include")
set(LOVR_PHYSICS_LIB ode)
endif()
add_subdirectory(deps/ode ode)
if(MSVC)
set_target_properties(ode PROPERTIES COMPILE_FLAGS "/wd4244 /wd4267")
target_compile_definitions(ode PRIVATE _CRT_SECURE_NO_WARNINGS)
else()
set_target_properties(ode PROPERTIES COMPILE_FLAGS "-Wno-unused-volatile-lvalue -Wno-array-bounds -Wno-undefined-var-template")
endif()
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include")
set(LOVR_ODE ode)
elseif(LOVR_PHYSICS_LIBRARY STREQUAL "JOLT")
set(DEBUG_RENDERER_IN_DEBUG_AND_RELEASE OFF CACHE BOOL "")
set(PROFILER_IN_DEBUG_AND_RELEASE OFF CACHE BOOL "")
add_subdirectory(deps/jolt-physics-sharp jolt)
set(LOVR_PHYSICS_LIB joltc)
endif()
endif()
@ -212,6 +224,7 @@ if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR)
set(LOVR_OPENXR ${OPENXR_LIBRARIES})
else()
set(DYNAMIC_LOADER ON CACHE BOOL "")
set(BUILD_WITH_WAYLAND_HEADERS OFF CACHE BOOL "")
include_directories(deps/openxr/include)
add_subdirectory(deps/openxr openxr)
set(LOVR_OPENXR openxr_loader)
@ -336,7 +349,7 @@ target_link_libraries(lovr
${LOVR_GLFW}
${LOVR_LUA}
${LOVR_MSDF}
${LOVR_ODE}
${LOVR_PHYSICS_LIB}
${LOVR_GLSLANG}
${LOVR_OPENXR}
${LOVR_OCULUS_AUDIO}
@ -350,6 +363,17 @@ if(LOVR_SANITIZE)
target_link_options(lovr PRIVATE ${LOVR_SANITIZE_FLAGS})
endif()
if(LOVR_PROFILE)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(FATAL_ERROR "You probably want to build in release mode when Tracy is enabled")
endif()
option(TRACY_ENABLE "" ON)
add_subdirectory(deps/tracy)
target_compile_definitions(lovr PRIVATE LOVR_PROFILE)
target_link_libraries(lovr Tracy::TracyClient)
target_include_directories(lovr PRIVATE deps/tracy/public/tracy)
endif()
if(LOVR_ENABLE_AUDIO OR LOVR_ENABLE_DATA)
target_sources(lovr PRIVATE
src/lib/miniaudio/miniaudio.c
@ -478,6 +502,7 @@ if(LOVR_ENABLE_GRAPHICS)
COMMAND
${GLSLANG_VALIDATOR}
--quiet
$<$<CONFIG:Debug>:-gVS>
--target-env vulkan1.1
--vn lovr_shader_${shader}_${ARGV0}
-o ${shader_file}.h
@ -532,13 +557,17 @@ endif()
if(LOVR_ENABLE_PHYSICS)
target_sources(lovr PRIVATE
src/modules/physics/physics.c
src/api/l_physics.c
src/api/l_physics_collider.c
src/api/l_physics_joints.c
src/api/l_physics_shapes.c
src/api/l_physics_world.c
)
if(LOVR_PHYSICS_LIBRARY STREQUAL "ODE")
target_sources(lovr PRIVATE src/modules/physics/physics_ode.c)
elseif(LOVR_PHYSICS_LIBRARY STREQUAL "JOLT")
target_sources(lovr PRIVATE src/modules/physics/physics_jolt.c)
endif()
else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_PHYSICS)
endif()
@ -558,6 +587,7 @@ endif()
if(LOVR_ENABLE_THREAD)
target_sources(lovr PRIVATE
src/core/job.c
src/modules/thread/thread.c
src/api/l_thread.c
src/api/l_thread_channel.c
@ -638,7 +668,7 @@ if(NOT ANDROID)
add_custom_command(TARGET lovr POST_BUILD
DEPENDS "etc/nogame"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/etc/nogame"
COMMAND ${CMAKE_COMMAND} -E tar c "${CMAKE_CURRENT_BINARY_DIR}/nogame.zip" --format=zip arg.lua conf.lua main.lua
COMMAND ${CMAKE_COMMAND} -E tar c "${CMAKE_CURRENT_BINARY_DIR}/nogame.zip" --format=zip .
COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/nogame.zip" >> ${NOGAME_BUNDLE}
)
endif()
@ -692,7 +722,7 @@ if(WIN32)
move_dll(${LOVR_GLFW})
move_dll(${LOVR_LUA})
move_dll(${LOVR_ODE})
move_dll(${LOVR_PHYSICS_LIB})
move_dll(${LOVR_MSDF})
move_dll(${LOVR_OCULUS_AUDIO})
move_dll(${LOVR_PHONON})
@ -743,7 +773,7 @@ elseif(APPLE)
endfunction()
move_lib(${LOVR_GLFW})
move_lib(${LOVR_LUA})
move_lib(${LOVR_ODE})
move_lib(${LOVR_PHYSICS_LIB})
move_lib(${LOVR_MSDF})
move_lib(${LOVR_OCULUS_AUDIO})
move_lib(${LOVR_PHONON})
@ -771,7 +801,7 @@ elseif(ANDROID)
# Dynamically linked targets output libraries in raw/lib/<ABI> for easy including in apk with aapt
set_target_properties(
lovr
${LOVR_ODE}
${LOVR_PHYSICS_LIB}
${LOVR_MSDF}
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/raw/lib/${ANDROID_ABI}"
)
@ -894,7 +924,7 @@ elseif(UNIX)
endfunction()
move_lib(${LOVR_GLFW})
move_lib(${LOVR_LUA})
move_lib(${LOVR_ODE})
move_lib(${LOVR_PHYSICS_LIB})
move_lib(${LOVR_MSDF})
move_lib(${LOVR_OPENXR})
move_lib(${LOVR_OCULUS_AUDIO})

View File

@ -8,6 +8,7 @@ config = {
glfw = true,
luajit = false,
glslang = true,
physics = 'ode',
utf8 = true,
modules = {
audio = true,
@ -96,7 +97,7 @@ flags = {
cflags = {
'-std=c11 -pedantic',
'-Wall -Wextra -Wno-unused-parameter',
'-Wall -Wextra -Wno-unused-parameter -Wno-strict-prototypes',
config.strict and '-Werror' or '',
config.optimize and '-fdata-sections -ffunction-sections' or '',
'-fdiagnostics-color=always',
@ -207,7 +208,7 @@ if config.luajit then
end
cflags += '-Ideps/luajit/src'
lflags += '-L' .. config.luajit
lflags += '-lluajit'
lflags += '-lluajit-5.1'
else
cflags += '-Ideps/lua'
lflags += '-llua'
@ -260,7 +261,6 @@ if config.modules.graphics and config.glslang then
glslang_cflags += '-fno-rtti'
glslang_cflags += '-Ideps/glslang'
glslang_lflags += '-shared'
glslang_src += 'deps/glslang/OGLCompilersDLL/*.cpp'
glslang_src += 'deps/glslang/glslang/CInterface/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/preprocessor/*.cpp'
@ -406,7 +406,11 @@ src = {
for module, enabled in pairs(config.modules) do
if enabled then
override = { audio = 'src/modules/audio/audio.c', headset = 'src/modules/headset/headset.c' } -- TODO
override = {
audio = 'src/modules/audio/audio.c',
headset = 'src/modules/headset/headset.c',
physics = ('src/modules/physics/physics_%s.c'):format(config.physics)
}
src += override[module] or ('src/modules/%s/*.c'):format(module)
src += ('src/api/l_%s*.c'):format(module)
else
@ -447,6 +451,7 @@ src += (config.modules.audio or config.modules.data) and 'src/lib/miniaudio/*.c'
src += config.modules.data and 'src/lib/jsmn/*.c' or nil
src += config.modules.data and 'src/lib/minimp3/*.c' or nil
src += config.modules.math and 'src/lib/noise/*.c' or nil
src += config.modules.thread and 'src/core/job.c' or nil
-- embed resource files with xxd
@ -463,9 +468,13 @@ vert = 'etc/shaders/*.vert'
frag = 'etc/shaders/*.frag'
comp = 'etc/shaders/*.comp'
glslang_flags += '--quiet'
glslang_flags += config.debug and '-gVS' or ''
glslang_flags += '--target-env vulkan1.1'
function compileShaders(stage)
pattern = 'etc/shaders/*.' .. stage
tup.foreach_rule(pattern, 'glslangValidator --quiet --target-env vulkan1.1 --vn lovr_shader_%B_' .. stage .. ' -o %o %f', '%f.h')
tup.foreach_rule(pattern, 'glslangValidator $(glslang_flags) --vn lovr_shader_%B_' .. stage .. ' -o %o %f', '%f.h')
end
compileShaders('vert')

2
deps/glslang vendored

@ -1 +1 @@
Subproject commit e7d1279bc02891585c96479987c0f3a11cc042b9
Subproject commit 5edafd3006723e9b4680d1cc03920a535460fed5

1
deps/jolt-physics-sharp vendored Submodule

@ -0,0 +1 @@
Subproject commit 2cda386d71174bec355888539679f7a4f7be1c2a

2
deps/openxr vendored

@ -1 +1 @@
Subproject commit 55224479ab13db8ebc8ab1e3d49197bce6201b0b
Subproject commit 288d3a7ebc1ad959f62d51da75baa3d27438c499

1
deps/tracy vendored Submodule

@ -0,0 +1 @@
Subproject commit a9288cd7594caced17899dda208b0d376b20d892

View File

@ -13,7 +13,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>v0.17.0</string>
<string>v0.17.1</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>

View File

@ -3,7 +3,7 @@ lovr = require 'lovr'
local lovr = lovr
local conf = {
version = '0.17.0',
version = '0.17.1',
identity = 'default',
saveprecedence = true,
modules = {
@ -41,6 +41,9 @@ local conf = {
math = {
globals = true
},
thread = {
workers = -1
},
window = {
width = 720,
height = 800,
@ -73,7 +76,7 @@ function lovr.boot()
'usage: lovr <source>',
'<source> can be a Lua file, a folder with a main.lua file, or a zip archive'
}, '\n'))
return 1
return 0
end
end
end
@ -194,7 +197,7 @@ local function formatTraceback(s)
end
function lovr.errhand(message)
message = 'Error:\n\n' .. tostring(message) .. formatTraceback(debug.traceback('', 4))
message = 'Error:\n\n' .. tostring(message) .. formatTraceback(debug and debug.traceback('', 4) or '')
print(message)
@ -289,7 +292,7 @@ lovr.handlers = setmetatable({}, { __index = lovr })
return coroutine.create(function()
local function onerror(...)
onerror = function(...)
print('Error:\n\n' .. tostring(...) .. formatTraceback(debug.traceback('', 1)))
print('Error:\n\n' .. tostring(...) .. formatTraceback(debug and debug.traceback('', 1) or ''))
return function() return 1 end
end

View File

@ -2,7 +2,7 @@
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
#include "shaders/lovr.glsl"
vec4 lovrmain() {
float y = UV.y;

View File

@ -6,5 +6,4 @@ function lovr.conf(t)
t.headset.supersample = true
t.modules.audio = false
t.modules.physics = false
t.modules.thread = false
end

BIN
etc/nogame/logo.spv Normal file

Binary file not shown.

View File

@ -9,7 +9,7 @@ function lovr.load()
lovr.graphics.setBackgroundColor(0x20232c)
end
logo = lovr.graphics.newShader('unlit', 'logo')
logo = lovr.graphics.newShader('unlit', 'logo.spv')
end
function lovr.draw(pass)

View File

@ -10,7 +10,6 @@
#include "shaders/animator.comp.h"
#include "shaders/blender.comp.h"
#include "shaders/tallymerge.comp.h"
#include "shaders/logo.frag.h"
#include "shaders/lovr.glsl.h"
@ -19,3 +18,5 @@
#define LOCATION_UV 12
#define LOCATION_COLOR 13
#define LOCATION_TANGENT 14
#define LAST_BUILTIN_BINDING 3

View File

@ -12,10 +12,10 @@ layout(push_constant) uniform PushConstants {
struct ModelVertex {
float x, y, z;
float nx, ny, nz;
uint normal;
float u, v;
uint color;
float tx, ty, tz, tw;
uint tangent;
};
struct SkinVertex {
@ -54,9 +54,6 @@ void lovrmain() {
vertexOut[vertexIndex].y = skinned.y;
vertexOut[vertexIndex].z = skinned.z;
vec3 normal = vec3(vertexIn[vertexIndex].nx, vertexIn[vertexIndex].ny, vertexIn[vertexIndex].nz);
vec3 skinnedNormal = mat3(matrix) * normal;
vertexOut[vertexIndex].nx = skinnedNormal.x;
vertexOut[vertexIndex].ny = skinnedNormal.y;
vertexOut[vertexIndex].nz = skinnedNormal.z;
vec3 normal = mat3(matrix) * normalize(unpackSnorm10x3(vertexIn[vertexIndex].normal).xyz);
vertexOut[vertexIndex].normal = packSnorm10x3(vec4(normal, 0.));
}

View File

@ -15,10 +15,10 @@ layout(push_constant) uniform PushConstants {
struct ModelVertex {
float px, py, pz;
float nx, ny, nz;
uint normal;
float u, v;
uint color;
float tx, ty, tz, tw;
uint tangent;
};
struct BlendVertex {
@ -39,6 +39,9 @@ void lovrmain() {
ModelVertex vertex = first ? rawVertices[vertexIndex] : vertices[vertexIndex];
vec4 normal = unpackSnorm10x3(vertex.normal);
vec4 tangent = unpackSnorm10x3(vertex.tangent);
for (uint i = 0; i < blendShapeCount; i++, blendVertexIndex += vertexCount) {
float weight = weights[i / 4][i % 4];
@ -52,14 +55,17 @@ void lovrmain() {
vertex.py += blendShape.py * weight;
vertex.pz += blendShape.pz * weight;
vertex.nx += blendShape.nx * weight;
vertex.ny += blendShape.ny * weight;
vertex.nz += blendShape.nz * weight;
normal.x += blendShape.nx * weight;
normal.y += blendShape.ny * weight;
normal.z += blendShape.nz * weight;
vertex.tx += blendShape.tx * weight;
vertex.ty += blendShape.ty * weight;
vertex.tz += blendShape.tz * weight;
tangent.x += blendShape.tx * weight;
tangent.y += blendShape.ty * weight;
tangent.z += blendShape.tz * weight;
}
vertex.normal = packSnorm10x3(normal);
vertex.tangent = packSnorm10x3(tangent);
vertices[vertexIndex] = vertex;
}

View File

@ -63,6 +63,10 @@ layout(set = 1, binding = 4) uniform texture2D RoughnessTexture;
layout(set = 1, binding = 5) uniform texture2D ClearcoatTexture;
layout(set = 1, binding = 6) uniform texture2D OcclusionTexture;
layout(set = 1, binding = 7) uniform texture2D NormalTexture;
layout(push_constant) uniform PushConstants {
uint DrawID;
};
#endif
// Attributes
@ -116,7 +120,7 @@ layout(location = 14) in vec4 Tangent;
#define BaseInstance gl_BaseInstance
#define BaseVertex gl_BaseVertex
#define DrawIndex gl_DrawIndex
#define InstanceIndex (gl_InstanceIndex - gl_BaseInstance)
#define InstanceIndex gl_InstanceIndex
#define PointSize gl_PointSize
#define Position gl_Position
#define VertexIndex gl_VertexIndex
@ -140,12 +144,7 @@ layout(location = 14) in vec4 Tangent;
// Helpers
#define Constants layout(push_constant) uniform PushConstants
#ifdef GL_COMPUTE_SHADER
#define var(x) layout(set = 0, binding = x)
#else
#define var(x) layout(set = 2, binding = x)
#endif
#define Constants uniform DefaultUniformBlock
#ifndef GL_COMPUTE_SHADER
#define Projection Cameras[ViewIndex].projection
@ -156,7 +155,6 @@ layout(location = 14) in vec4 Tangent;
#endif
#ifdef GL_VERTEX_SHADER
#define DrawID gl_BaseInstance
#define Transform mat4(Draws[DrawID].transform)
#define NormalMatrix (cofactor3(Draws[DrawID].transform))
#define PassColor Draws[DrawID].color
@ -444,6 +442,24 @@ vec3 linearToGamma(vec3 color) {
return mix(1.055 * pow(color, vec3(1. / 2.4)) - .055, color * 12.92, lessThanEqual(color, vec3(.0031308)));
}
uint packSnorm10x3(vec4 v) {
return
((int(v.x * 511.) & 0x3ff) << 0) |
((int(v.y * 511.) & 0x3ff) << 10) |
((int(v.z * 511.) & 0x3ff) << 20) |
((int(v.w * 2.) & 0x3) << 30);
}
// The weird 22 bit shift basically does sign-extension of a 10-bit value stored in a 32-bit int
vec4 unpackSnorm10x3(uint n) {
return vec4(
max((int((n >> 0) & 0x3ff) << 22 >> 22) / 511., -1.),
max((int((n >> 10) & 0x3ff) << 22 >> 22) / 511., -1.),
max((int((n >> 20) & 0x3ff) << 22 >> 22) / 511., -1.),
max(float((n >> 30) & 0x3), -1.)
);
}
// Entrypoints
#ifndef NO_DEFAULT_MAIN
#ifdef GL_VERTEX_SHADER

@ -1 +1 @@
Subproject commit 69906e40f571bcbdda00fe2e6f504c3487f93d56
Subproject commit c18a947289a8df0903d52786e6a535d3dad85d65

@ -1 +1 @@
Subproject commit daaee4ef0056958c33b204a83a97c770ff0a2cbc
Subproject commit 2be3778e3f2447df079212f69d56a49cd9185f3d

View File

@ -152,6 +152,10 @@ void _luax_registertype(lua_State* L, const char* name, const luaL_Reg* function
lua_pushcfunction(L, luax_meta__gc);
lua_setfield(L, -2, "__gc");
// m.__close = gc
lua_pushcfunction(L, luax_meta__gc);
lua_setfield(L, -2, "__close");
// m.__tostring
lua_pushcfunction(L, luax_meta__tostring);
lua_setfield(L, -2, "__tostring");
@ -382,7 +386,7 @@ void luax_pushconf(lua_State* L) {
int luax_setconf(lua_State* L) {
luax_pushconf(L);
lovrAssert(lua_isnil(L, -1), "Unable to set lovr.conf multiple times");
lovrCheck(lua_isnil(L, -1), "Unable to set lovr.conf multiple times");
lua_pop(L, 1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrconf");
return 0;
@ -511,19 +515,13 @@ 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) {
if (lua_istable(L, index)) {
luaL_checktype(L, index + 1, LUA_TTABLE);
lua_rawgeti(L, index, 1);
bool nested = lua_type(L, -1) == LUA_TTABLE;
lua_pop(L, 1);
*vertexCount = luax_len(L, index) / (nested ? 1 : 3);
*indexCount = luax_len(L, index + 1);
lovrAssert(*vertexCount > 0, "Invalid mesh data: vertex count is zero");
lovrAssert(*indexCount > 0, "Invalid mesh data: index count is zero");
lovrAssert(*indexCount % 3 == 0, "Index count must be a multiple of 3");
*vertices = malloc(sizeof(float) * *vertexCount * 3);
*indices = malloc(sizeof(uint32_t) * *indexCount);
lovrAssert(vertices && indices, "Out of memory");
lovrCheck(*vertexCount > 0, "Invalid mesh data: vertex count is zero");
*vertices = lovrMalloc(sizeof(float) * *vertexCount * 3);
*shouldFree = true;
if (nested) {
@ -545,17 +543,33 @@ int luax_readmesh(lua_State* L, int index, float** vertices, uint32_t* vertexCou
}
}
for (uint32_t i = 0; i < *indexCount; i++) {
lua_rawgeti(L, index + 1, i + 1);
uint32_t index = luaL_checkinteger(L, -1) - 1;
lovrAssert(index < *vertexCount, "Invalid vertex index %d (expected [%d, %d])", index + 1, 1, *vertexCount);
(*indices)[i] = index;
lua_pop(L, 1);
if (indices) {
luaL_checktype(L, index + 1, LUA_TTABLE);
*indexCount = luax_len(L, index + 1);
lovrCheck(*indexCount > 0, "Invalid mesh data: index count is zero");
lovrCheck(*indexCount % 3 == 0, "Index count must be a multiple of 3");
*indices = lovrMalloc(sizeof(uint32_t) * *indexCount);
for (uint32_t i = 0; i < *indexCount; i++) {
lua_rawgeti(L, index + 1, i + 1);
uint32_t index = luaL_checkinteger(L, -1) - 1;
lovrCheck(index < *vertexCount, "Invalid vertex index %d (expected [%d, %d])", index + 1, 1, *vertexCount);
(*indices)[i] = index;
lua_pop(L, 1);
}
}
return index + 2;
}
ModelData* modelData = luax_totype(L, index, ModelData);
if (modelData) {
lovrModelDataGetTriangles(modelData, vertices, indices, vertexCount, indexCount);
*shouldFree = false;
return index + 1;
}
#ifndef LOVR_DISABLE_GRAPHICS
Model* model = luax_totype(L, index, Model);
@ -573,7 +587,9 @@ int luax_readmesh(lua_State* L, int index, float** vertices, uint32_t* vertexCou
*shouldFree = true;
return index + 1;
}
#endif
return luaL_argerror(L, index, "table, Mesh, or Model");
return luaL_argerror(L, index, "table, ModelData, Model, or Mesh expected");
#else
return luaL_argerror(L, index, "table or ModelData expected");
#endif
}

View File

@ -51,6 +51,7 @@ extern StringEntry lovrPassType[];
extern StringEntry lovrPermission[];
extern StringEntry lovrSampleFormat[];
extern StringEntry lovrShaderStage[];
extern StringEntry lovrShaderType[];
extern StringEntry lovrShapeType[];
extern StringEntry lovrSmoothMode[];
extern StringEntry lovrStackType[];
@ -200,6 +201,8 @@ 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_newconvexshape(lua_State* L, int index);
struct Shape* luax_newmeshshape(lua_State* L, int index);
struct Shape* luax_newterrainshape(lua_State* L, int index);
struct Shape* luax_newcompoundshape(lua_State* L, int index);
#endif

View File

@ -200,8 +200,8 @@ static int l_lovrAudioSetGeometry(lua_State* L) {
AudioMaterial material = luax_checkenum(L, index, AudioMaterial, "generic");
bool success = lovrAudioSetGeometry(vertices, indices, vertexCount, indexCount, material);
if (shouldFree) {
free(vertices);
free(indices);
lovrFree(vertices);
lovrFree(indices);
}
lua_pushboolean(L, success);
return 1;
@ -256,7 +256,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
lua_getfield(L, 2, "effects");
if (!lua_isnil(L, -1)) {
effects = 0;
lovrAssert(lua_istable(L, -1), "Source effects must be a table");
lovrCheck(lua_istable(L, -1), "Source effects must be a table");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
if (lua_type(L, -2) == LUA_TSTRING) {
@ -277,18 +277,19 @@ static int l_lovrAudioNewSource(lua_State* L) {
lua_pop(L, 1);
}
uint32_t defer = lovrDeferPush();
if (!sound) {
Blob* blob = luax_readblob(L, 1, "Source");
lovrDeferRelease(blob, lovrBlobDestroy);
sound = lovrSoundCreateFromFile(blob, decode);
lovrRelease(blob, lovrBlobDestroy);
} else {
lovrRetain(sound);
lovrDeferRelease(sound, lovrSoundDestroy);
}
Source* source = lovrSourceCreate(sound, pitchable, spatial, effects);
luax_pushtype(L, Source, source);
lovrRelease(sound, lovrSoundDestroy);
lovrRelease(source, lovrSourceDestroy);
lovrDeferPop(defer);
return 1;
}

View File

@ -64,8 +64,10 @@ Image* luax_checkimage(lua_State* L, int index) {
lovrRetain(image);
} else {
Blob* blob = luax_readblob(L, index, "Image");
uint32_t defer = lovrDeferPush();
lovrDeferRelease(blob, lovrBlobDestroy);
image = lovrImageCreateFromFile(blob);
lovrRelease(blob, lovrBlobDestroy);
lovrDeferPop(defer);
}
return image;
@ -77,21 +79,18 @@ static int l_lovrDataNewBlob(lua_State* L) {
int type = lua_type(L, 1);
if (type == LUA_TNUMBER) {
int isize = lua_tonumber(L, 1);
lovrAssert(isize > 0, "Blob size must be positive");
lovrCheck(isize > 0, "Blob size must be positive");
size = (size_t) isize;
data = calloc(1, size);
lovrAssert(data, "Out of memory");
data = lovrCalloc(size);
} else if (type == LUA_TSTRING) {
const char* str = luaL_checklstring(L, 1, &size);
data = malloc(size + 1);
lovrAssert(data, "Out of memory");
data = lovrMalloc(size + 1);
memcpy(data, str, size);
data[size] = '\0';
} else {
Blob* blob = luax_checktype(L, 1, Blob);
size = blob->size;
data = malloc(size);
lovrAssert(data, "Out of memory");
data = lovrMalloc(size);
memcpy(data, blob->data, size);
}
const char* name = luaL_optstring(L, 2, "");
@ -139,10 +138,12 @@ static int l_lovrDataNewImage(lua_State* L) {
static int l_lovrDataNewModelData(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Model");
uint32_t defer = lovrDeferPush();
lovrDeferRelease(blob, lovrBlobDestroy);
ModelData* modelData = lovrModelDataCreate(blob, luax_readfile);
luax_pushtype(L, ModelData, modelData);
lovrRelease(blob, lovrBlobDestroy);
lovrRelease(modelData, lovrModelDataDestroy);
lovrDeferPop(defer);
return 1;
}
@ -150,22 +151,26 @@ static int l_lovrDataNewRasterizer(lua_State* L) {
Blob* blob = NULL;
float size;
uint32_t defer = lovrDeferPush();
if (lua_type(L, 1) == LUA_TNUMBER || lua_isnoneornil(L, 1)) {
size = luax_optfloat(L, 1, 32.f);
} else {
blob = luax_readblob(L, 1, "Font");
size = luax_optfloat(L, 2, 32.f);
lovrDeferRelease(blob, lovrBlobDestroy);
}
Rasterizer* rasterizer = lovrRasterizerCreate(blob, size);
luax_pushtype(L, Rasterizer, rasterizer);
lovrRelease(blob, lovrBlobDestroy);
lovrRelease(rasterizer, lovrRasterizerDestroy);
lovrDeferPop(defer);
return 1;
}
static int l_lovrDataNewSound(lua_State* L) {
int type = lua_type(L, 1);
if (type == LUA_TNUMBER) {
uint32_t frames = luax_checku32(L, 1);
SampleFormat format = luax_checkenum(L, 2, SampleFormat, "f32");
@ -186,10 +191,13 @@ static int l_lovrDataNewSound(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Sound");
bool decode = lua_toboolean(L, 2);
uint32_t defer = lovrDeferPush();
lovrDeferRelease(blob, lovrBlobDestroy);
Sound* sound = lovrSoundCreateFromFile(blob, decode);
luax_pushtype(L, Sound, sound);
lovrRelease(blob, lovrBlobDestroy);
lovrRelease(sound, lovrSoundDestroy);
lovrDeferPop(defer);
return 1;
}

View File

@ -21,6 +21,7 @@ StringEntry lovrTextureFormat[] = {
[FORMAT_RGB10A2] = ENTRY("rgb10a2"),
[FORMAT_RG11B10F] = ENTRY("rg11b10f"),
[FORMAT_D16] = ENTRY("d16"),
[FORMAT_D24] = ENTRY("d24"),
[FORMAT_D32F] = ENTRY("d32f"),
[FORMAT_D24S8] = ENTRY("d24s8"),
[FORMAT_D32FS8] = ENTRY("d32fs8"),

View File

@ -109,7 +109,7 @@ static int l_lovrSoundGetFrames(lua_State* L) {
uint32_t dstOffset = luax_optu32(L, index + 2, 0);
uint32_t srcOffset = luax_optu32(L, index + 1, 0);
uint32_t count = luax_optu32(L, index, frameCount - srcOffset);
lovrAssert(srcOffset + count <= frameCount, "Tried to read samples past the end of the Sound");
lovrCheck(srcOffset + count <= frameCount, "Tried to read samples past the end of the Sound");
lua_settop(L, 2);
switch (lua_type(L, 2)) {
@ -151,7 +151,7 @@ static int l_lovrSoundGetFrames(lua_State* L) {
Sound* other = luax_totype(L, 2, Sound);
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
lovrAssert(dstOffset + count * stride <= blob->size, "This Blob can hold %d bytes, which is not enough space to hold %d bytes of audio data at the requested offset (%d)", blob->size, count * stride, dstOffset);
lovrCheck(dstOffset + count * stride <= blob->size, "This Blob can hold %d bytes, which is not enough space to hold %d bytes of audio data at the requested offset (%d)", blob->size, count * stride, dstOffset);
char* data = (char*) blob->data + dstOffset;
uint32_t frames = 0;
while (frames < count) {
@ -215,7 +215,7 @@ static int l_lovrSoundSetFrames(lua_State* L) {
uint32_t dstOffset = luax_optu32(L, 4, 0);
uint32_t limit = MIN(frameCount - dstOffset, (length - srcOffset) / channels + 1);
uint32_t count = luax_optu32(L, 3, limit);
lovrAssert(count <= limit, "Tried to write too many frames (%d is over limit %d)", count, limit);
lovrCheck(count <= limit, "Tried to write too many frames (%d is over limit %d)", count, limit);
uint32_t frames = 0;
while (frames < count) {

View File

@ -55,8 +55,7 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant) {
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");
variant->value.string.pointer = lovrMalloc(length + 1);
memcpy(variant->value.string.pointer, string, length);
variant->value.string.pointer[length] = '\0';
variant->value.string.length = length;
@ -92,8 +91,7 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant) {
if (v) {
if (type == V_MAT4) {
variant->type = TYPE_MATRIX;
variant->value.matrix.data = malloc(16 * sizeof(float));
lovrAssert(variant->value.matrix.data, "Out of memory");
variant->value.matrix.data = lovrMalloc(16 * sizeof(float));
memcpy(variant->value.matrix.data, v, 16 * sizeof(float));
break;
} else {
@ -205,7 +203,7 @@ static int nextEvent(lua_State* L) {
luax_pushtype(L, Thread, event.data.thread.thread);
lua_pushstring(L, event.data.thread.error);
lovrRelease(event.data.thread.thread, lovrThreadDestroy);
free(event.data.thread.error);
lovrFree(event.data.thread.error);
return 3;
#endif

View File

@ -74,7 +74,7 @@ static int luax_loadfile(lua_State* L, const char* path, const char* debug, cons
return 2;
}
int status = luax_loadbufferx(L, buffer, size, debug, mode);
free(buffer);
lovrFree(buffer);
switch (status) {
case LUA_ERRMEM: return luaL_error(L, "Memory allocation error: %s", lua_tostring(L, -1));
case LUA_ERRSYNTAX: return luaL_error(L, "Syntax error: %s", lua_tostring(L, -1));
@ -315,7 +315,7 @@ static int l_lovrFilesystemRead(lua_State* L) {
return 1;
}
lua_pushlstring(L, data, size);
free(data);
lovrFree(data);
return 1;
}

View File

@ -41,17 +41,18 @@ static int l_lovrFileRead(lua_State* L) {
size = lovrFileGetSize(file) - lovrFileTell(file);
}
size_t count;
void* data = malloc(size);
lovrAssert(data, "Out of memory");
void* data = lovrMalloc(size);
uint32_t defer = lovrDeferPush();
lovrDefer(lovrFree, data);
bool success = lovrFileRead(file, data, size, &count);
if (success) {
lua_pushlstring(L, data, count);
lua_pushnumber(L, count);
free(data);
lovrDeferPop(defer);
return 2;
} else {
lua_pushnil(L);
free(data);
lovrDeferPop(defer);
return 1;
}
}

View File

@ -58,7 +58,6 @@ StringEntry lovrDefaultShader[] = {
[SHADER_EQUIRECT] = ENTRY("equirect"),
[SHADER_FILL_2D] = ENTRY("fill"),
[SHADER_FILL_ARRAY] = ENTRY("fillarray"),
[SHADER_LOGO] = ENTRY("logo"),
{ 0 }
};
@ -80,6 +79,7 @@ StringEntry lovrDataType[] = {
[TYPE_U8x4] = ENTRY("u8x4"),
[TYPE_SN8x4] = ENTRY("sn8x4"),
[TYPE_UN8x4] = ENTRY("un8x4"),
[TYPE_SN10x3] = ENTRY("sn10x3"),
[TYPE_UN10x3] = ENTRY("un10x3"),
[TYPE_I16] = ENTRY("i16"),
[TYPE_I16x2] = ENTRY("i16x2"),
@ -145,6 +145,12 @@ StringEntry lovrShaderStage[] = {
{ 0 }
};
StringEntry lovrShaderType[] = {
[SHADER_GRAPHICS] = ENTRY("graphics"),
[SHADER_COMPUTE] = ENTRY("compute"),
{ 0 }
};
StringEntry lovrStackType[] = {
[STACK_TRANSFORM] = ENTRY("transform"),
[STACK_STATE] = ENTRY("state"),
@ -299,19 +305,14 @@ static void luax_writeshadercache(void) {
return;
}
void* data = malloc(size);
if (!data) {
return;
}
void* data = lovrMalloc(size);
lovrGraphicsGetShaderCache(data, &size);
if (size > 0) {
luax_writefile(".lovrshadercache", data, size);
}
free(data);
lovrFree(data);
}
static int l_lovrGraphicsInitialize(lua_State* L) {
@ -349,8 +350,11 @@ static int l_lovrGraphicsInitialize(lua_State* L) {
}
lua_pop(L, 2);
uint32_t defer = lovrDeferPush();
if (shaderCache) {
config.cacheData = luax_readfile(".lovrshadercache", &config.cacheSize);
lovrDefer(lovrFree, config.cacheData);
}
lovrGraphicsInit(&config);
@ -360,7 +364,8 @@ static int l_lovrGraphicsInitialize(lua_State* L) {
luax_atexit(L, luax_writeshadercache);
}
free(config.cacheData);
lovrDeferPop(defer);
return 0;
}
@ -388,8 +393,13 @@ static int l_lovrGraphicsSubmit(lua_State* L) {
uint32_t count = 0;
Pass* stack[8];
Pass** passes = (size_t) length > COUNTOF(stack) ? malloc(length * sizeof(Pass*)) : stack;
lovrAssert(passes, "Out of memory");
Pass** passes = stack;
uint32_t defer = lovrDeferPush();
if ((size_t) length > COUNTOF(stack)) {
passes = lovrMalloc(length * sizeof(Pass*));
lovrDefer(lovrFree, passes);
}
if (table) {
for (int i = 0; i < length; i++) {
@ -408,8 +418,8 @@ static int l_lovrGraphicsSubmit(lua_State* L) {
}
lovrGraphicsSubmit(passes, count);
if (passes != stack) free(passes);
lua_pushboolean(L, true);
lovrDeferPop(defer);
return 1;
}
@ -719,13 +729,19 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
return 1;
}
static void freeImages(void* arg) {
TextureInfo* info = arg;
for (uint32_t i = 0; i < info->imageCount; i++) {
lovrRelease(info->images[i], lovrImageDestroy);
}
}
static int l_lovrGraphicsNewTexture(lua_State* L) {
TextureInfo info = {
.type = TEXTURE_2D,
.format = FORMAT_RGBA8,
.layers = 1,
.mipmaps = ~0u,
.samples = 1,
.usage = TEXTURE_SAMPLE,
.srgb = true
};
@ -733,6 +749,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
int index = 1;
Image* stack[6];
Image** images = stack;
uint32_t defer = lovrDeferPush();
if (lua_isnumber(L, 1)) {
info.width = luax_checku32(L, index++);
@ -744,13 +761,18 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
info.usage |= TEXTURE_RENDER;
info.mipmaps = 1;
} else if (lua_istable(L, 1)) {
info.imageCount = luax_len(L, index++);
images = info.imageCount > COUNTOF(stack) ? malloc(info.imageCount * sizeof(Image*)) : stack;
lovrAssert(images, "Out of memory");
int tableLength = luax_len(L, index++);
if (info.imageCount == 0) {
if ((size_t) tableLength > COUNTOF(stack)) {
images = lovrMalloc(tableLength * sizeof(Image*));
lovrDefer(lovrFree, images);
}
info.images = images;
lovrDefer(freeImages, &info);
if (tableLength == 0) {
info.layers = 6;
info.imageCount = 6;
info.type = TEXTURE_CUBE;
const char* faces[6] = { "right", "left", "top", "bottom", "back", "front" };
const char* altFaces[6] = { "px", "nx", "py", "ny", "pz", "nz" };
@ -762,13 +784,13 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
lua_pushstring(L, altFaces[i]);
lua_rawget(L, 1);
}
lovrAssert(!lua_isnil(L, -1), "No array texture layers given and cubemap face '%s' missing", faces[i]);
images[i] = luax_checkimage(L, -1);
lovrCheck(!lua_isnil(L, -1), "No array texture layers given and cubemap face '%s' missing", faces[i]);
images[info.imageCount++] = luax_checkimage(L, -1);
}
} else {
for (uint32_t i = 0; i < info.imageCount; i++) {
for (int i = 0; i < tableLength; i++) {
lua_rawgeti(L, 1, i + 1);
images[i] = luax_checkimage(L, -1);
images[info.imageCount++] = luax_checkimage(L, -1);
lua_pop(L, 1);
}
@ -787,7 +809,6 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
}
if (info.imageCount > 0) {
info.images = images;
Image* image = images[0];
uint32_t levels = lovrImageGetLevelCount(image);
info.format = lovrImageGetFormat(image);
@ -797,11 +818,11 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
bool mipmappable = lovrGraphicsGetFormatSupport(info.format, TEXTURE_FEATURE_BLIT) & (1 << info.srgb);
info.mipmaps = (levels == 1 && mipmappable) ? ~0u : levels;
for (uint32_t i = 1; i < info.imageCount; i++) {
lovrAssert(lovrImageGetWidth(images[0], 0) == lovrImageGetWidth(images[i], 0), "Image widths must match");
lovrAssert(lovrImageGetHeight(images[0], 0) == lovrImageGetHeight(images[i], 0), "Image heights must match");
lovrAssert(lovrImageGetFormat(images[0]) == lovrImageGetFormat(images[i]), "Image formats must match");
lovrAssert(lovrImageGetLevelCount(images[0]) == lovrImageGetLevelCount(images[i]), "Image mipmap counts must match");
lovrAssert(lovrImageGetLayerCount(images[i]) == 1, "When a list of images are provided, each must have a single layer");
lovrCheck(lovrImageGetWidth(images[0], 0) == lovrImageGetWidth(images[i], 0), "Image widths must match");
lovrCheck(lovrImageGetHeight(images[0], 0) == lovrImageGetHeight(images[i], 0), "Image heights must match");
lovrCheck(lovrImageGetFormat(images[0]) == lovrImageGetFormat(images[i]), "Image formats must match");
lovrCheck(lovrImageGetLevelCount(images[0]) == lovrImageGetLevelCount(images[i]), "Image mipmap counts must match");
lovrCheck(lovrImageGetLayerCount(images[i]) == 1, "When a list of images are provided, each must have a single layer");
}
}
@ -814,10 +835,6 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
lua_getfield(L, index, "format");
info.format = lua_isnil(L, -1) ? info.format : (uint32_t) luax_checkenum(L, -1, TextureFormat, NULL);
lua_pop(L, 1);
lua_getfield(L, index, "samples");
info.samples = lua_isnil(L, -1) ? info.samples : luax_checku32(L, -1);
lua_pop(L, 1);
}
lua_getfield(L, index, "linear");
@ -831,7 +848,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
} else if (!lua_isnil(L, -1)) {
info.mipmaps = lua_toboolean(L, -1) ? ~0u : 1;
} else {
info.mipmaps = (info.samples > 1 || info.imageCount == 0 || !mipmappable) ? 1 : ~0u;
info.mipmaps = (info.imageCount == 0 || !mipmappable) ? 1 : ~0u;
}
lovrCheck(info.imageCount == 0 || info.mipmaps == 1 || mipmappable, "This texture format does not support blitting, which is required for mipmap generation");
lua_pop(L, 1);
@ -863,17 +880,46 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
}
Texture* texture = lovrTextureCreate(&info);
for (uint32_t i = 0; i < info.imageCount; i++) {
lovrRelease(images[i], lovrImageDestroy);
}
if (images != stack) {
free(images);
}
luax_pushtype(L, Texture, texture);
lovrRelease(texture, lovrTextureDestroy);
lovrDeferPop(defer);
return 1;
}
static int l_lovrGraphicsNewTextureView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* base = lovrTextureGetInfo(texture);
luaL_checktype(L, 2, LUA_TTABLE);
TextureViewInfo info = { 0 };
lua_getfield(L, 2, "type");
info.type = lua_isnil(L, -1) ? base->type : luax_checkenum(L, -1, TextureType, NULL);
lua_pop(L, 1);
lua_getfield(L, 2, "layer");
info.layerIndex = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1) - 1;
lua_pop(L, 1);
lua_getfield(L, 2, "layercount");
info.layerCount = lua_isnil(L, -1) ? ~0u : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "mipmap");
info.levelIndex = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1) - 1;
lua_pop(L, 1);
lua_getfield(L, 2, "mipmapcount");
info.levelCount = lua_isnil(L, -1) ? ~0u : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "label");
info.label = lua_tostring(L, -1);
lua_pop(L, 1);
Texture* view = lovrTextureCreateView(texture, &info);
luax_pushtype(L, Texture, view);
lovrRelease(view, lovrTextureDestroy);
return 1;
}
@ -930,7 +976,7 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
lua_getfield(L, 1, "mipmaprange");
if (!lua_isnil(L, -1)) {
lovrAssert(lua_istable(L, -1), "Sampler mipmap range must be nil or a table");
lovrCheck(lua_istable(L, -1), "Sampler mipmap range must be nil or a table");
lua_rawgeti(L, -1, 1);
lua_rawgeti(L, -2, 2);
info.range[0] = luax_checkfloat(L, -2);
@ -945,96 +991,90 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
return 1;
}
static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* allocated) {
ShaderSource source;
static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* shouldFree) {
*shouldFree = false;
if (lua_isstring(L, index)) {
size_t length;
const char* string = lua_tolstring(L, index, &length);
if (memchr(string, '\n', MIN(256, length))) {
source.code = string;
source.size = length;
*allocated = false;
return (ShaderSource) { stage, string, length };
} else {
for (int i = 0; lovrDefaultShader[i].length; i++) {
if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) {
*allocated = false;
return lovrGraphicsGetDefaultShaderSource(i, stage);
}
}
source.code = luax_readfile(string, &source.size);
size_t size;
void* code = luax_readfile(string, &size);
if (source.code) {
*allocated = true;
if (code) {
*shouldFree = true;
return (ShaderSource) { stage, code, size };
} else {
luaL_argerror(L, index, "single-line string was not filename or DefaultShader");
}
}
} else if (lua_isuserdata(L, index)) {
Blob* blob = luax_checktype(L, index, Blob);
source.code = blob->data;
source.size = blob->size;
*allocated = false;
return (ShaderSource) { stage, blob->data, blob->size };
} else {
*allocated = false;
return lovrGraphicsGetDefaultShaderSource(SHADER_UNLIT, stage);
luax_typeerror(L, index, "string, Blob, or DefaultShader");
}
ShaderSource bytecode = lovrGraphicsCompileShader(stage, &source, luax_readfile);
if (bytecode.code != source.code) {
if (*allocated) free((void*) source.code);
*allocated = true;
return bytecode;
}
return source;
return (ShaderSource) { 0 };
}
static int l_lovrGraphicsCompileShader(lua_State* L) {
ShaderStage stage = luax_checkenum(L, 1, ShaderStage, NULL);
bool allocated;
ShaderSource spirv = luax_checkshadersource(L, 2, stage, &allocated);
Blob* blob = lovrBlobCreate((void*) spirv.code, spirv.size, "Compiled Shader Code");
luax_pushtype(L, Blob, blob);
lovrRelease(blob, lovrBlobDestroy);
return 1;
ShaderSource inputs[2], outputs[2];
bool shouldFree[2];
uint32_t count;
if (lua_gettop(L) == 1) {
inputs[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
count = 1;
} else {
inputs[0] = luax_checkshadersource(L, 1, STAGE_VERTEX, &shouldFree[0]);
inputs[1] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &shouldFree[1]);
count = 2;
}
lovrGraphicsCompileShader(inputs, outputs, count, luax_readfile);
for (uint32_t i = 0; i < count; i++) {
if (shouldFree[i] && outputs[i].code != inputs[i].code) lovrFree((void*) inputs[i].code);
Blob* blob = lovrBlobCreate((void*) outputs[i].code, outputs[i].size, "Shader code");
luax_pushtype(L, Blob, blob);
lovrRelease(blob, lovrBlobDestroy);
}
return count;
}
static int l_lovrGraphicsNewShader(lua_State* L) {
ShaderInfo info = { 0 };
bool allocated[2];
ShaderSource source[2], compiled[2];
ShaderInfo info = { .stages = compiled };
bool shouldFree[2] = { 0 };
int index;
// If there's only one source given, it could be a DefaultShader or a compute shader
if (lua_gettop(L) == 1 || (lua_istable(L, 2) && luax_len(L, 2) == 0)) {
if (lua_type(L, 1) == LUA_TSTRING) {
size_t length;
const char* string = lua_tolstring(L, 1, &length);
for (int i = 0; lovrDefaultShader[i].length; i++) {
if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) {
info.source[STAGE_VERTEX] = lovrGraphicsGetDefaultShaderSource(i, STAGE_VERTEX);
info.source[STAGE_FRAGMENT] = lovrGraphicsGetDefaultShaderSource(i, STAGE_FRAGMENT);
allocated[0] = false;
allocated[1] = false;
break;
}
}
}
if (!info.source[0].code) {
info.source[STAGE_COMPUTE] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &allocated[0]);
}
if (lua_gettop(L) == 1 || lua_istable(L, 2)) {
info.type = SHADER_COMPUTE;
source[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
info.stageCount = 1;
index = 2;
} else {
info.source[STAGE_VERTEX] = luax_checkshadersource(L, 1, STAGE_VERTEX, &allocated[0]);
info.source[STAGE_FRAGMENT] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &allocated[1]);
info.type = SHADER_GRAPHICS;
source[0] = luax_checkshadersource(L, 1, STAGE_VERTEX, &shouldFree[0]);
source[1] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &shouldFree[1]);
info.stageCount = 2;
index = 3;
}
lovrGraphicsCompileShader(source, compiled, info.stageCount, luax_readfile);
arr_t(ShaderFlag) flags;
arr_init(&flags, realloc);
arr_init(&flags);
if (lua_istable(L, index)) {
lua_getfield(L, index, "flags");
@ -1055,6 +1095,10 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
}
lua_pop(L, 1);
lua_getfield(L, index, "type");
info.type = lua_isnil(L, -1) ? info.type : luax_checkenum(L, -1, ShaderType, NULL);
lua_pop(L, 1);
lua_getfield(L, index, "label");
info.label = lua_tostring(L, -1);
lua_pop(L, 1);
@ -1068,8 +1112,12 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
Shader* shader = lovrShaderCreate(&info);
luax_pushtype(L, Shader, shader);
lovrRelease(shader, lovrShaderDestroy);
if (allocated[0]) free((void*) info.source[0].code);
if (allocated[1]) free((void*) info.source[1].code);
for (uint32_t i = 0; i < info.stageCount; i++) {
if (shouldFree[i]) lovrFree((void*) source[i].code);
if (source[i].code != compiled[i].code) lovrFree((void*) compiled[i].code);
}
arr_free(&flags);
return 1;
}
@ -1091,15 +1139,16 @@ static Texture* luax_opttexture(lua_State* L, int index) {
.height = lovrImageGetHeight(image, 0),
.layers = 1,
.mipmaps = ~0u,
.samples = 1,
.usage = TEXTURE_SAMPLE,
.srgb = lovrImageIsSRGB(image),
.imageCount = 1,
.images = &image
};
uint32_t defer = lovrDeferPush();
lovrDeferRelease(image, lovrImageDestroy);
texture = lovrTextureCreate(&info);
lovrRelease(image, lovrImageDestroy);
lovrDeferPop(defer);
return texture;
}
@ -1228,6 +1277,8 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
info.rasterizer = luax_totype(L, 1, Rasterizer);
info.spread = 4.;
uint32_t defer = lovrDeferPush();
if (!info.rasterizer) {
Blob* blob = NULL;
float size;
@ -1239,19 +1290,19 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
blob = luax_readblob(L, 1, "Font");
size = luax_optfloat(L, 2, 32.);
info.spread = luaL_optnumber(L, 3, info.spread);
lovrDeferRelease(blob, lovrBlobDestroy);
}
info.rasterizer = lovrRasterizerCreate(blob, size);
lovrRelease(blob, lovrBlobDestroy);
lovrDeferRelease(info.rasterizer, lovrRasterizerDestroy);
} else {
info.spread = luaL_optnumber(L, 2, info.spread);
lovrRetain(info.rasterizer);
}
Font* font = lovrFontCreate(&info);
luax_pushtype(L, Font, font);
lovrRelease(info.rasterizer, lovrRasterizerDestroy);
lovrRelease(font, lovrFontDestroy);
lovrDeferPop(defer);
return 1;
}
@ -1365,12 +1416,13 @@ static int l_lovrGraphicsNewModel(lua_State* L) {
info.materials = true;
info.mipmaps = true;
uint32_t defer = lovrDeferPush();
if (!info.data) {
Blob* blob = luax_readblob(L, 1, "Model");
lovrDeferRelease(blob, lovrBlobDestroy);
info.data = lovrModelDataCreate(blob, luax_readfile);
lovrRelease(blob, lovrBlobDestroy);
} else {
lovrRetain(info.data);
lovrDeferRelease(info.data, lovrModelDataDestroy);
}
if (lua_istable(L, 2)) {
@ -1385,8 +1437,8 @@ static int l_lovrGraphicsNewModel(lua_State* L) {
Model* model = lovrModelCreate(&info);
luax_pushtype(L, Model, model);
lovrRelease(info.data, lovrModelDataDestroy);
lovrRelease(model, lovrModelDestroy);
lovrDeferPop(defer);
return 1;
}
@ -1420,6 +1472,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "getDefaultFont", l_lovrGraphicsGetDefaultFont },
{ "newBuffer", l_lovrGraphicsNewBuffer },
{ "newTexture", l_lovrGraphicsNewTexture },
{ "newTextureView", l_lovrGraphicsNewTextureView },
{ "newSampler", l_lovrGraphicsNewSampler },
{ "compileShader", l_lovrGraphicsCompileShader },
{ "newShader", l_lovrGraphicsNewShader },

View File

@ -10,6 +10,7 @@ static const uint32_t typeComponents[] = {
[TYPE_U8x4] = 4,
[TYPE_SN8x4] = 4,
[TYPE_UN8x4] = 4,
[TYPE_SN10x3] = 3,
[TYPE_UN10x3] = 3,
[TYPE_I16] = 1,
[TYPE_I16x2] = 2,
@ -70,7 +71,8 @@ void luax_checkfieldn(lua_State* L, int index, int type, void* data) {
case TYPE_U8x4: p.u8[i] = (uint8_t) x; break;
case TYPE_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
case TYPE_UN8x4: p.u8[i] = (uint8_t) CLAMP(x, 0.f, 1.f) * UINT8_MAX; break;
case TYPE_UN10x3: p.u32[0] |= (uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case TYPE_SN10x3: p.u32[0] |= (((uint32_t) (int32_t) (CLAMP(x, -1.f, 1.f) * 511.f)) & 0x3ff) << (10 * i); break;
case TYPE_UN10x3: p.u32[0] |= (((uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f)) & 0x3ff) << (10 * i); break;
case TYPE_I16: p.i16[i] = (int16_t) x; break;
case TYPE_I16x2: p.i16[i] = (int16_t) x; break;
case TYPE_I16x4: p.i16[i] = (int16_t) x; break;
@ -122,7 +124,8 @@ void luax_checkfieldv(lua_State* L, int index, int type, void* data) {
case TYPE_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
case TYPE_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
case TYPE_UN8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) CLAMP(v[i], 0.f, 1.f) * UINT8_MAX; break;
case TYPE_UN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (uint32_t) (CLAMP(v[i], 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case TYPE_SN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (((uint32_t) (int32_t) (CLAMP(v[i], -1.f, 1.f) * 511.f)) & 0x3ff) << (10 * i); break;
case TYPE_UN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (((uint32_t) (CLAMP(v[i], 0.f, 1.f) * 1023.f)) & 0x3ff) << (10 * i); break;
case TYPE_I16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) v[i]; break;
case TYPE_I16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) v[i]; break;
case TYPE_U16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) v[i]; break;
@ -325,7 +328,8 @@ static int luax_pushcomponents(lua_State* L, DataType type, char* data) {
case TYPE_U8x4: for (int i = 0; i < 4; i++) lua_pushinteger(L, p.u8[i]); return 4;
case TYPE_SN8x4: for (int i = 0; i < 4; i++) lua_pushnumber(L, MAX((float) p.i8[i] / 127, -1.f)); return 4;
case TYPE_UN8x4: for (int i = 0; i < 4; i++) lua_pushnumber(L, (float) p.u8[i] / 255); return 4;
case TYPE_UN10x3: for (int i = 0; i < 3; i++) lua_pushnumber(L, (float) ((p.u32[0] >> (10 * (2 - i))) & 0x3ff) / 1023.f); return 3;
case TYPE_SN10x3: for (int i = 0; i < 3; i++) lua_pushnumber(L, (float) ((p.i32[0] >> (10 * i)) & 0x3ff) / 511.f); return 3;
case TYPE_UN10x3: for (int i = 0; i < 3; i++) lua_pushnumber(L, (float) ((p.u32[0] >> (10 * i)) & 0x3ff) / 1023.f); return 3;
case TYPE_I16x2: for (int i = 0; i < 2; i++) lua_pushinteger(L, p.i16[i]); return 2;
case TYPE_I16x4: for (int i = 0; i < 4; i++) lua_pushinteger(L, p.i16[i]); return 4;
case TYPE_U16x2: for (int i = 0; i < 2; i++) lua_pushinteger(L, p.u16[i]); return 2;

View File

@ -7,8 +7,7 @@
ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, ColoredString* stack) {
if (lua_istable(L, index)) {
*count = luax_len(L, index) / 2;
ColoredString* strings = malloc(*count * sizeof(*strings));
lovrAssert(strings, "Out of memory");
ColoredString* strings = lovrMalloc(*count * sizeof(*strings));
for (uint32_t i = 0; i < *count; i++) {
lua_rawgeti(L, index, i * 2 + 1);
lua_rawgeti(L, index, i * 2 + 2);
@ -123,7 +122,7 @@ static int l_lovrFontGetLines(lua_State* L) {
float wrap = luax_checkfloat(L, 3);
lua_newtable(L);
lovrFontGetLines(font, strings, 1, wrap, online, L);
if (strings != &stack) free(strings);
if (strings != &stack) lovrFree(strings);
return 1;
}
@ -139,8 +138,7 @@ static int l_lovrFontGetVertices(lua_State* L) {
for (uint32_t i = 0; i < count; i++) {
totalLength += strings[i].length;
}
GlyphVertex* vertices = malloc(totalLength * 4 * sizeof(GlyphVertex));
lovrAssert(vertices, "Out of memory");
GlyphVertex* vertices = lovrMalloc(totalLength * 4 * sizeof(GlyphVertex));
uint32_t glyphCount, lineCount;
Material* material;
lovrFontGetVertices(font, strings, count, wrap, halign, valign, vertices, &glyphCount, &lineCount, &material, false);
@ -159,8 +157,8 @@ static int l_lovrFontGetVertices(lua_State* L) {
lua_rawseti(L, -2, i + 1);
}
luax_pushtype(L, Material, material);
if (strings != &stack) free(strings);
free(vertices);
if (strings != &stack) lovrFree(strings);
lovrFree(vertices);
return 2;
}

View File

@ -340,7 +340,7 @@ static int l_lovrModelGetMeshCount(lua_State* L) {
static int l_lovrModelGetMesh(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luax_checku32(L, 3) - 1;
uint32_t index = luax_checku32(L, 2) - 1;
Mesh* mesh = lovrModelGetMesh(model, index);
luax_pushtype(L, Mesh, mesh);
return 1;

View File

@ -643,15 +643,9 @@ static int l_lovrPassSetWireframe(lua_State* L) {
static int l_lovrPassSend(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
const char* name = NULL;
size_t length = 0;
uint32_t slot = ~0u;
switch (lua_type(L, 2)) {
case LUA_TSTRING: name = lua_tolstring(L, 2, &length); break;
case LUA_TNUMBER: slot = lua_tointeger(L, 2) - 1; break;
default: return luax_typeerror(L, 2, "string or number");
}
size_t length;
const char* name = lua_tolstring(L, 2, &length);
if (lua_isnoneornil(L, 3)) {
return luax_typeerror(L, 3, "Buffer, Texture, Sampler, number, vector, table, or boolean");
@ -662,27 +656,27 @@ static int l_lovrPassSend(lua_State* L) {
if (buffer) {
uint32_t offset = lua_tointeger(L, 4);
uint32_t extent = lua_tointeger(L, 5);
lovrPassSendBuffer(pass, name, length, slot, buffer, offset, extent);
lovrPassSendBuffer(pass, name, length, buffer, offset, extent);
return 0;
}
Texture* texture = luax_totype(L, 3, Texture);
if (texture) {
lovrPassSendTexture(pass, name, length, slot, texture);
lovrPassSendTexture(pass, name, length, texture);
return 0;
}
Sampler* sampler = luax_totype(L, 3, Sampler);
if (sampler) {
lovrPassSendSampler(pass, name, length, slot, sampler);
lovrPassSendSampler(pass, name, length, sampler);
return 0;
}
void* pointer;
DataField* format;
lovrPassSendData(pass, name, length, slot, &pointer, &format);
lovrPassSendData(pass, name, length, &pointer, &format);
char* data = pointer;
// Coerce booleans since they aren't supported in buffer formats
@ -869,7 +863,7 @@ static int l_lovrPassSphere(lua_State* L) {
int index = luax_readmat4(L, 2, transform, 1);
uint32_t segmentsH = luax_optu32(L, index++, 48);
uint32_t segmentsV = luax_optu32(L, index++, segmentsH / 2);
lovrAssert(segmentsH >= 2 && segmentsV >= 2, "Number of longitudes and latitudes must be >= 2");
lovrCheck(segmentsH >= 2 && segmentsV >= 2, "Sphere segment count must be >= 2");
lovrPassSphere(pass, transform, segmentsH, segmentsV);
return 0;
}
@ -943,13 +937,15 @@ static int l_lovrPassText(lua_State* L) {
uint32_t count;
ColoredString stack;
ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack);
uint32_t defer = lovrDeferPush();
if (strings != &stack) lovrDefer(lovrFree, strings);
float transform[16];
int index = luax_readmat4(L, 3, transform, 1);
float wrap = luax_optfloat(L, index++, 0.);
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
VerticalAlign valign = luax_checkenum(L, index++, VerticalAlign, "middle");
lovrPassText(pass, strings, count, transform, wrap, halign, valign);
if (strings != &stack) free(strings);
lovrDeferPop(defer);
return 0;
}

View File

@ -9,7 +9,7 @@ static int l_lovrShaderClone(lua_State* L) {
lua_pushnil(L);
arr_t(ShaderFlag) flags;
arr_init(&flags, realloc);
arr_init(&flags);
while (lua_next(L, 2) != 0) {
ShaderFlag flag = { 0 };
@ -33,6 +33,13 @@ static int l_lovrShaderClone(lua_State* L) {
return 1;
}
static int l_lovrShaderGetType(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const ShaderInfo* info = lovrShaderGetInfo(shader);
luax_pushenum(L, ShaderType, info->type);
return 1;
}
static int l_lovrShaderHasStage(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL);
@ -76,28 +83,34 @@ static int l_lovrShaderGetWorkgroupSize(lua_State* L) {
static int l_lovrShaderGetBufferFormat(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
uint32_t fieldCount;
const DataField* format = lovrShaderGetBufferFormat(shader, name, &fieldCount);
lovrCheck(format, "Shader has no Buffer named '%s'", name);
if (!format) {
lua_pushnil(L);
return 1;
}
luax_pushbufferformat(L, format->fields, format->fieldCount);
lua_pushinteger(L, format->stride);
lua_setfield(L, -2, "stride");
lua_pushinteger(L, MAX(format->length, 1));
return 2;
}
// Deprecated
static int l_lovrShaderGetType(lua_State* L) {
lua_pushstring(L, lovrShaderHasStage(luax_checktype(L, 1, Shader), STAGE_COMPUTE) ? "compute" : "graphics");
return 1;
if (format->length == ~0u) {
lua_pushnil(L);
} else {
lua_pushinteger(L, MAX(format->length, 1));
}
return 2;
}
const luaL_Reg lovrShader[] = {
{ "clone", l_lovrShaderClone },
{ "getType", l_lovrShaderGetType },
{ "hasStage", l_lovrShaderHasStage },
{ "hasAttribute", l_lovrShaderHasAttribute },
{ "getWorkgroupSize", l_lovrShaderGetWorkgroupSize },
{ "getBufferFormat", l_lovrShaderGetBufferFormat },
{ "getType", l_lovrShaderGetType }, // Deprecated
{ NULL, NULL }
};

View File

@ -4,41 +4,6 @@
#include "data/image.h"
#include <string.h>
static int l_lovrTextureNewView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
TextureViewInfo info = { .parent = texture };
if (lua_type(L, 2) == LUA_TNUMBER) {
info.type = TEXTURE_2D;
info.layerIndex = luax_checku32(L, 2) - 1;
info.layerCount = 1;
info.levelIndex = luax_optu32(L, 3, 1) - 1;
info.levelCount = 1;
} else if (lua_isstring(L, 2)) {
info.type = luax_checkenum(L, 2, TextureType, NULL);
info.layerIndex = luaL_optinteger(L, 3, 1) - 1;
info.layerCount = luaL_optinteger(L, 4, 1);
info.levelIndex = luaL_optinteger(L, 5, 1) - 1;
info.levelCount = luaL_optinteger(L, 6, 0);
}
Texture* view = lovrTextureCreateView(&info);
luax_pushtype(L, Texture, view);
lovrRelease(view, lovrTextureDestroy);
return 1;
}
static int l_lovrTextureIsView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushboolean(L, !!lovrTextureGetInfo(texture)->parent);
return 1;
}
static int l_lovrTextureGetParent(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture);
luax_pushtype(L, Texture, info->parent);
return 1;
}
static int l_lovrTextureGetType(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture);
@ -91,10 +56,8 @@ static int l_lovrTextureGetMipmapCount(lua_State* L) {
return 1;
}
static int l_lovrTextureGetSampleCount(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture);
lua_pushinteger(L, info->samples);
static int l_lovrTextureGetSampleCount(lua_State* L) { // Deprecated
lua_pushinteger(L, 1);
return 1;
}
@ -236,9 +199,6 @@ static int l_lovrTextureGenerateMipmaps(lua_State* L) {
}
const luaL_Reg lovrTexture[] = {
{ "newView", l_lovrTextureNewView },
{ "isView", l_lovrTextureIsView },
{ "getParent", l_lovrTextureGetParent },
{ "getType", l_lovrTextureGetType },
{ "getFormat", l_lovrTextureGetFormat },
{ "getWidth", l_lovrTextureGetWidth },
@ -246,7 +206,7 @@ const luaL_Reg lovrTexture[] = {
{ "getLayerCount", l_lovrTextureGetLayerCount },
{ "getDimensions", l_lovrTextureGetDimensions },
{ "getMipmapCount", l_lovrTextureGetMipmapCount },
{ "getSampleCount", l_lovrTextureGetSampleCount },
{ "getSampleCount", l_lovrTextureGetSampleCount }, // Deprecated
{ "hasUsage", l_lovrTextureHasUsage },
{ "newReadback", l_lovrTextureNewReadback },
{ "getPixels", l_lovrTextureGetPixels },

View File

@ -572,10 +572,12 @@ static int l_lovrHeadsetNewModel(lua_State* L) {
if (modelData) {
ModelInfo info = { .data = modelData, .mipmaps = true };
uint32_t defer = lovrDeferPush();
lovrDeferRelease(info.data, lovrModelDataDestroy);
Model* model = lovrModelCreate(&info);
luax_pushtype(L, Model, model);
lovrRelease(modelData, lovrModelDataDestroy);
lovrRelease(model, lovrModelDestroy);
lovrDeferPop(defer);
return 1;
}
@ -784,7 +786,7 @@ int luaopen_lovr_headset(lua_State* L) {
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
config.drivers[config.driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL);
lovrAssert(config.driverCount < COUNTOF(drivers), "Too many headset drivers specified in conf.lua");
lovrCheck(config.driverCount < COUNTOF(drivers), "Too many headset drivers specified in conf.lua");
lua_pop(L, 1);
}
lua_pop(L, 1);

View File

@ -32,13 +32,14 @@ extern const luaL_Reg lovrQuat[];
extern const luaL_Reg lovrMat4[];
static LOVR_THREAD_LOCAL Pool* pool;
static LOVR_THREAD_LOCAL int metaref[MAX_VECTOR_TYPES];
static struct { const char* name; lua_CFunction constructor, indexer; const luaL_Reg* api; int metaref; } lovrVectorInfo[] = {
[V_VEC2] = { "vec2", l_lovrMathVec2, l_lovrVec2__metaindex, lovrVec2, LUA_REFNIL },
[V_VEC3] = { "vec3", l_lovrMathVec3, l_lovrVec3__metaindex, lovrVec3, LUA_REFNIL },
[V_VEC4] = { "vec4", l_lovrMathVec4, l_lovrVec4__metaindex, lovrVec4, LUA_REFNIL },
[V_QUAT] = { "quat", l_lovrMathQuat, l_lovrQuat__metaindex, lovrQuat, LUA_REFNIL },
[V_MAT4] = { "mat4", l_lovrMathMat4, l_lovrMat4__metaindex, lovrMat4, LUA_REFNIL }
static struct { const char* name; lua_CFunction constructor, indexer; const luaL_Reg* api; } lovrVectorInfo[] = {
[V_VEC2] = { "vec2", l_lovrMathVec2, l_lovrVec2__metaindex, lovrVec2 },
[V_VEC3] = { "vec3", l_lovrMathVec3, l_lovrVec3__metaindex, lovrVec3 },
[V_VEC4] = { "vec4", l_lovrMathVec4, l_lovrVec4__metaindex, lovrVec4 },
[V_QUAT] = { "quat", l_lovrMathQuat, l_lovrQuat__metaindex, lovrQuat },
[V_MAT4] = { "mat4", l_lovrMathMat4, l_lovrMat4__metaindex, lovrMat4 }
};
static void luax_destroypool(void) {
@ -78,7 +79,7 @@ float* luax_checkvector(lua_State* L, int index, VectorType type, const char* ex
static float* luax_newvector(lua_State* L, VectorType type, size_t components) {
VectorType* p = lua_newuserdata(L, sizeof(VectorType) + components * sizeof(float));
*p = type;
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_rawgeti(L, LUA_REGISTRYINDEX, metaref[type]);
lua_setmetatable(L, -2);
return (float*) (p + 1);
}
@ -110,7 +111,7 @@ static int l_lovrMathNewCurve(lua_State* L) {
} else if (top == 1 && lua_type(L, 1) == LUA_TNUMBER) {
float point[4] = { 0.f };
int count = lua_tonumber(L, 1);
lovrAssert(count > 0, "Number of curve points must be positive");
lovrCheck(count > 0, "Number of curve points must be positive");
for (int i = 0; i < count; i++) {
lovrCurveAddPoint(curve, point, i);
}
@ -302,7 +303,7 @@ static int l_lovrLightUserdata__index(lua_State* L) {
return 0;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_rawgeti(L, LUA_REGISTRYINDEX, metaref[type]);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
@ -334,7 +335,7 @@ static int l_lovrLightUserdataOp(lua_State* L) {
return luaL_error(L, "Unsupported lightuserdata operator %q", lua_tostring(L, lua_upvalueindex(1)));
}
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_rawgeti(L, LUA_REGISTRYINDEX, metaref[type]);
lua_pushvalue(L, lua_upvalueindex(1));
lua_gettable(L, -2);
lua_pushvalue(L, 1);
@ -369,7 +370,7 @@ int luaopen_lovr_math(lua_State* L) {
lua_settable(L, -4);
luax_register(L, lovrVectorInfo[i].api);
lovrVectorInfo[i].metaref = luaL_ref(L, LUA_REGISTRYINDEX);
metaref[i] = luaL_ref(L, LUA_REGISTRYINDEX);
}
// Global lightuserdata metatable

View File

@ -64,7 +64,7 @@ static int l_lovrCurveGetPointCount(lua_State* L) {
static int l_lovrCurveGetPoint(lua_State* L) {
Curve* curve = luax_checktype(L, 1, Curve);
size_t index = luaL_checkinteger(L, 2) - 1;
lovrAssert(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCheck(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
float point[4];
lovrCurveGetPoint(curve, index, point);
lua_pushnumber(L, point[0]);
@ -76,7 +76,7 @@ static int l_lovrCurveGetPoint(lua_State* L) {
static int l_lovrCurveSetPoint(lua_State* L) {
Curve* curve = luax_checktype(L, 1, Curve);
size_t index = luaL_checkinteger(L, 2) - 1;
lovrAssert(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCheck(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
float point[4];
luax_readvec3(L, 3, point, NULL);
lovrCurveSetPoint(curve, index, point);
@ -88,7 +88,7 @@ static int l_lovrCurveAddPoint(lua_State* L) {
float point[4];
int i = luax_readvec3(L, 2, point, NULL);
size_t index = lua_isnoneornil(L, i) ? lovrCurveGetPointCount(curve) : (size_t) luaL_checkinteger(L, i) - 1;
lovrAssert(index <= lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCheck(index <= lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCurveAddPoint(curve, point, index);
return 0;
}
@ -96,7 +96,7 @@ static int l_lovrCurveAddPoint(lua_State* L) {
static int l_lovrCurveRemovePoint(lua_State* L) {
Curve* curve = luax_checktype(L, 1, Curve);
size_t index = luaL_checkinteger(L, 2) - 1;
lovrAssert(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCheck(index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1);
lovrCurveRemovePoint(curve, index);
return 0;
}

View File

@ -7,8 +7,10 @@ StringEntry lovrShapeType[] = {
[SHAPE_BOX] = ENTRY("box"),
[SHAPE_CAPSULE] = ENTRY("capsule"),
[SHAPE_CYLINDER] = ENTRY("cylinder"),
[SHAPE_CONVEX] = ENTRY("convex"),
[SHAPE_MESH] = ENTRY("mesh"),
[SHAPE_TERRAIN] = ENTRY("terrain"),
[SHAPE_COMPOUND] = ENTRY("compound"),
{ 0 }
};
@ -21,27 +23,75 @@ StringEntry lovrJointType[] = {
};
static int l_lovrPhysicsNewWorld(lua_State* L) {
float xg = luax_optfloat(L, 1, 0.f);
float yg = luax_optfloat(L, 2, -9.81f);
float zg = luax_optfloat(L, 3, 0.f);
bool allowSleep = lua_gettop(L) < 4 || lua_toboolean(L, 4);
const char* tags[16];
int tagCount;
if (lua_type(L, 5) == LUA_TTABLE) {
tagCount = luax_len(L, 5);
for (int i = 0; i < tagCount; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_isstring(L, -1)) {
tags[i] = lua_tostring(L, -1);
} else {
return luaL_error(L, "World tags must be a table of strings");
WorldInfo info = {
.maxColliders = 65536,
.maxColliderPairs = 65536,
.maxContacts = 16384,
.allowSleep = true
};
if (lua_istable(L, 1)) {
lua_getfield(L, 1, "maxColliders");
if (!lua_isnil(L, -1)) info.maxColliders = luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "maxColliderPairs");
if (!lua_isnil(L, -1)) info.maxColliderPairs = luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "maxContacts");
if (!lua_isnil(L, -1)) info.maxContacts = luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "allowSleep");
if (!lua_isnil(L, -1)) info.allowSleep = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "tags");
if (!lua_isnil(L, -1)) {
lovrCheck(lua_istable(L, -1), "World tag list should be a table");
lovrCheck(info.tagCount <= MAX_TAGS, "Max number of world tags is %d", MAX_TAGS);
info.tagCount = luax_len(L, 5);
for (uint32_t i = 0; i < info.tagCount; i++) {
lua_rawgeti(L, -1, (int) i + 1);
if (lua_isstring(L, -1)) {
info.tags[i] = lua_tostring(L, -1);
} else {
return luaL_error(L, "World tags must be a table of strings");
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
} else {
tagCount = 0;
lua_pop(L, 1);
} else { // Deprecated
info.allowSleep = lua_gettop(L) < 4 || lua_toboolean(L, 4);
if (lua_type(L, 5) == LUA_TTABLE) {
info.tagCount = luax_len(L, 5);
lovrCheck(info.tagCount <= MAX_TAGS, "Max number of world tags is %d", MAX_TAGS);
for (uint32_t i = 0; i < info.tagCount; i++) {
lua_rawgeti(L, -1, (int) i + 1);
if (lua_isstring(L, -1)) {
info.tags[i] = lua_tostring(L, -1);
} else {
return luaL_error(L, "World tags must be a table of strings");
}
lua_pop(L, 1);
}
} else {
info.tagCount = 0;
}
}
World* world = lovrWorldCreate(xg, yg, zg, allowSleep, tags, tagCount);
World* world = lovrWorldCreate(&info);
if (!lua_istable(L, 1)) {
float gravity[3];
gravity[0] = luax_optfloat(L, 1, 0.f);
gravity[1] = luax_optfloat(L, 2, -9.81f);
gravity[2] = luax_optfloat(L, 3, 0.f);
lovrWorldSetGravity(world, gravity);
}
luax_pushtype(L, World, world);
lovrRelease(world, lovrWorldDestroy);
return 1;
@ -72,6 +122,13 @@ static int l_lovrPhysicsNewCapsuleShape(lua_State* L) {
return 1;
}
static int l_lovrPhysicsNewConvexShape(lua_State* L) {
ConvexShape* convex = luax_newconvexshape(L, 1);
luax_pushtype(L, ConvexShape, convex);
lovrRelease(convex, lovrShapeDestroy);
return 1;
}
static int l_lovrPhysicsNewCylinderShape(lua_State* L) {
CylinderShape* cylinder = luax_newcylindershape(L, 1);
luax_pushtype(L, CylinderShape, cylinder);
@ -135,11 +192,19 @@ static int l_lovrPhysicsNewTerrainShape(lua_State* L) {
return 1;
}
static int l_lovrPhysicsNewCompoundShape(lua_State* L) {
CompoundShape* shape = luax_newcompoundshape(L, 1);
luax_pushtype(L, CompoundShape, shape);
lovrRelease(shape, lovrShapeDestroy);
return 1;
}
static const luaL_Reg lovrPhysics[] = {
{ "newWorld", l_lovrPhysicsNewWorld },
{ "newBallJoint", l_lovrPhysicsNewBallJoint },
{ "newBoxShape", l_lovrPhysicsNewBoxShape },
{ "newCapsuleShape", l_lovrPhysicsNewCapsuleShape },
{ "newConvexShape", l_lovrPhysicsNewConvexShape },
{ "newCylinderShape", l_lovrPhysicsNewCylinderShape },
{ "newDistanceJoint", l_lovrPhysicsNewDistanceJoint },
{ "newHingeJoint", l_lovrPhysicsNewHingeJoint },
@ -147,6 +212,7 @@ static const luaL_Reg lovrPhysics[] = {
{ "newSliderJoint", l_lovrPhysicsNewSliderJoint },
{ "newSphereShape", l_lovrPhysicsNewSphereShape },
{ "newTerrainShape", l_lovrPhysicsNewTerrainShape },
{ "newCompoundShape", l_lovrPhysicsNewCompoundShape },
{ NULL, NULL }
};
@ -160,8 +226,10 @@ extern const luaL_Reg lovrSphereShape[];
extern const luaL_Reg lovrBoxShape[];
extern const luaL_Reg lovrCapsuleShape[];
extern const luaL_Reg lovrCylinderShape[];
extern const luaL_Reg lovrConvexShape[];
extern const luaL_Reg lovrMeshShape[];
extern const luaL_Reg lovrTerrainShape[];
extern const luaL_Reg lovrCompoundShape[];
int luaopen_lovr_physics(lua_State* L) {
lua_newtable(L);
@ -176,8 +244,10 @@ int luaopen_lovr_physics(lua_State* L) {
luax_registertype(L, BoxShape);
luax_registertype(L, CapsuleShape);
luax_registertype(L, CylinderShape);
luax_registertype(L, ConvexShape);
luax_registertype(L, MeshShape);
luax_registertype(L, TerrainShape);
luax_registertype(L, CompoundShape);
lovrPhysicsInit();
luax_atexit(L, lovrPhysicsDestroy);
return 1;

View File

@ -17,6 +17,20 @@ static int l_lovrColliderIsDestroyed(lua_State* L) {
return 1;
}
static int l_lovrColliderIsEnabled(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool enabled = lovrColliderIsEnabled(collider);
lua_pushboolean(L, enabled);
return 1;
}
static int l_lovrColliderSetEnabled(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool enable = lua_toboolean(L, 2);
lovrColliderSetEnabled(collider, enable);
return 1;
}
static int l_lovrColliderGetWorld(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
World* world = lovrColliderGetWorld(collider);
@ -24,65 +38,94 @@ static int l_lovrColliderGetWorld(lua_State* L) {
return 1;
}
static int l_lovrColliderAddShape(lua_State* L) {
static int l_lovrColliderGetShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checkshape(L, 2);
lovrColliderAddShape(collider, shape);
return 0;
}
static int l_lovrColliderRemoveShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checkshape(L, 2);
lovrColliderRemoveShape(collider, shape);
return 0;
}
static int l_lovrColliderGetShapes(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
size_t count;
Shape** shapes = lovrColliderGetShapes(collider, &count);
lua_createtable(L, (int) count, 0);
for (size_t i = 0; i < count; i++) {
luax_pushshape(L, shapes[i]);
lua_rawseti(L, -2, (int) i + 1);
uint32_t child = lua_gettop(L) == 1 ? ~0u : luax_checku32(L, 2) - 1;
Shape* shape = lovrColliderGetShape(collider, child);
if (shape) {
luax_pushshape(L, shape);
} else {
lua_pushnil(L);
}
return 1;
}
static int l_lovrColliderSetShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = lua_isnoneornil(L, 2) ? NULL : luax_checkshape(L, 2);
lovrColliderSetShape(collider, shape);
return 0;
}
static int l_lovrColliderGetShapeOffset(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float position[3], orientation[4], angle, ax, ay, az;
lovrColliderGetShapeOffset(collider, position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrColliderSetShapeOffset(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
int index = 2;
float position[3], orientation[4];
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, orientation, NULL);
lovrColliderSetShapeOffset(collider, position, orientation);
return 0;
}
static int l_lovrColliderGetJoints(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
size_t count;
Joint** joints = lovrColliderGetJoints(collider, &count);
lua_createtable(L, (int) count, 0);
for (size_t i = 0; i < count; i++) {
luax_pushjoint(L, joints[i]);
lua_rawseti(L, -2, (int) i + 1);
lua_newtable(L);
int index = 1;
Joint* joint = NULL;
while ((joint = lovrColliderGetJoints(collider, joint)) != NULL) {
luax_pushjoint(L, joint);
lua_rawseti(L, -2, index++);
}
return 1;
}
static void luax_pushcolliderstash(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrcolliderstash");
if (lua_isnil(L, -1)) {
lua_newtable(L);
lua_replace(L, -2);
// metatable
lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrcolliderstash");
}
}
static int l_lovrColliderGetUserData(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
union { int i; void* p; } ref = { .p = lovrColliderGetUserData(collider) };
lua_rawgeti(L, LUA_REGISTRYINDEX, ref.i);
luax_checktype(L, 1, Collider);
luax_pushcolliderstash(L);
lua_pushvalue(L, 1);
lua_rawget(L, -2);
return 1;
}
static int l_lovrColliderSetUserData(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
union { int i; void* p; } ref = { .p = lovrColliderGetUserData(collider) };
if (ref.i) {
luaL_unref(L, LUA_REGISTRYINDEX, ref.i);
}
if (lua_gettop(L) < 2) {
lua_pushnil(L);
}
lua_settop(L, 2);
ref.i = luaL_ref(L, LUA_REGISTRYINDEX);
lovrColliderSetUserData(collider, ref.p);
luax_checktype(L, 1, Collider);
luax_pushcolliderstash(L);
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_rawset(L, -3);
return 0;
}
@ -99,16 +142,44 @@ static int l_lovrColliderSetKinematic(lua_State* L) {
return 0;
}
static int l_lovrColliderIsGravityIgnored(lua_State* L) {
static int l_lovrColliderIsSensor(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
lua_pushboolean(L, lovrColliderIsGravityIgnored(collider));
lua_pushboolean(L, lovrColliderIsSensor(collider));
return 1;
}
static int l_lovrColliderSetGravityIgnored(lua_State* L) {
static int l_lovrColliderSetSensor(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool ignored = lua_toboolean(L, 2);
lovrColliderSetGravityIgnored(collider, ignored);
bool sensor = lua_toboolean(L, 2);
lovrColliderSetSensor(collider, sensor);
return 0;
}
static int l_lovrColliderIsContinuous(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool continuous = lovrColliderIsContinuous(collider);
lua_pushboolean(L, continuous);
return 1;
}
static int l_lovrColliderSetContinuous(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool continuous = lua_toboolean(L, 2);
lovrColliderSetContinuous(collider, continuous);
return 0;
}
static int l_lovrColliderGetGravityScale(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float scale = lovrColliderGetGravityScale(collider);
lua_pushnumber(L, scale);
return 1;
}
static int l_lovrColliderSetGravityScale(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float scale = luax_checkfloat(L, 2);
lovrColliderSetGravityScale(collider, scale);
return 0;
}
@ -153,12 +224,11 @@ static int l_lovrColliderSetMass(lua_State* L) {
static int l_lovrColliderGetMassData(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float cx, cy, cz, mass;
float inertia[6];
lovrColliderGetMassData(collider, &cx, &cy, &cz, &mass, inertia);
lua_pushnumber(L, cx);
lua_pushnumber(L, cy);
lua_pushnumber(L, cz);
float centerOfMass[3], mass, inertia[6];
lovrColliderGetMassData(collider, centerOfMass, &mass, inertia);
lua_pushnumber(L, centerOfMass[0]);
lua_pushnumber(L, centerOfMass[1]);
lua_pushnumber(L, centerOfMass[2]);
lua_pushnumber(L, mass);
lua_newtable(L);
for (int i = 0; i < 6; i++) {
@ -170,14 +240,13 @@ static int l_lovrColliderGetMassData(lua_State* L) {
static int l_lovrColliderSetMassData(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float cx = luax_checkfloat(L, 2);
float cy = luax_checkfloat(L, 3);
float cz = luax_checkfloat(L, 4);
float mass = luax_checkfloat(L, 5);
float centerOfMass[3];
int index = luax_readvec3(L, 2, centerOfMass, NULL);
float mass = luax_checkfloat(L, index++);
float inertia[6];
if (lua_istable(L, 6) && luax_len(L, 6) >= 6) {
if (lua_istable(L, index) && luax_len(L, index) >= 6) {
for (int i = 0; i < 6; i++) {
lua_rawgeti(L, 6, i + 1);
lua_rawgeti(L, index, i + 1);
if (!lua_isnumber(L, -1)) {
return luaL_argerror(L, 6, "Expected 6 numbers or a table with 6 numbers");
}
@ -186,25 +255,25 @@ static int l_lovrColliderSetMassData(lua_State* L) {
lua_pop(L, 1);
}
} else {
for (int i = 6; i < 12; i++) {
for (int i = index; i < index + 6; i++) {
if (lua_isnumber(L, i)) {
inertia[i - 6] = lua_tonumber(L, i);
inertia[i - index] = lua_tonumber(L, i);
} else {
return luaL_argerror(L, i, "Expected 6 numbers or a table with 6 numbers");
}
}
}
lovrColliderSetMassData(collider, cx, cy, cz, mass, inertia);
lovrColliderSetMassData(collider, centerOfMass, mass, inertia);
return 0;
}
static int l_lovrColliderGetPosition(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float x, y, z;
lovrColliderGetPosition(collider, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float position[3];
lovrColliderGetPosition(collider, position);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
return 3;
}
@ -212,13 +281,13 @@ static int l_lovrColliderSetPosition(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrColliderSetPosition(collider, position[0], position[1], position[2]);
lovrColliderSetPosition(collider, position);
return 0;
}
static int l_lovrColliderGetOrientation(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float angle, x, y, z, orientation[4];
float orientation[4], angle, x, y, z;
lovrColliderGetOrientation(collider, orientation);
quat_getAngleAxis(orientation, &angle, &x, &y, &z);
lua_pushnumber(L, angle);
@ -238,13 +307,13 @@ static int l_lovrColliderSetOrientation(lua_State* L) {
static int l_lovrColliderGetPose(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float x, y, z, angle, ax, ay, az, orientation[4];
lovrColliderGetPosition(collider, &x, &y, &z);
float position[3], orientation[4], angle, ax, ay, az;
lovrColliderGetPosition(collider, position);
lovrColliderGetOrientation(collider, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
@ -257,18 +326,18 @@ static int l_lovrColliderSetPose(lua_State* L) {
float position[3], orientation[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readquat(L, index, orientation, NULL);
lovrColliderSetPosition(collider, position[0], position[1], position[2]);
lovrColliderSetPosition(collider, position);
lovrColliderSetOrientation(collider, orientation);
return 0;
}
static int l_lovrColliderGetLinearVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float x, y, z;
lovrColliderGetLinearVelocity(collider, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float velocity[3];
lovrColliderGetLinearVelocity(collider, velocity);
lua_pushnumber(L, velocity[0]);
lua_pushnumber(L, velocity[1]);
lua_pushnumber(L, velocity[2]);
return 3;
}
@ -276,17 +345,17 @@ static int l_lovrColliderSetLinearVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float velocity[3];
luax_readvec3(L, 2, velocity, NULL);
lovrColliderSetLinearVelocity(collider, velocity[0], velocity[1], velocity[2]);
lovrColliderSetLinearVelocity(collider, velocity);
return 0;
}
static int l_lovrColliderGetAngularVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float x, y, z;
lovrColliderGetAngularVelocity(collider, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float velocity[3];
lovrColliderGetAngularVelocity(collider, velocity);
lua_pushnumber(L, velocity[0]);
lua_pushnumber(L, velocity[1]);
lua_pushnumber(L, velocity[2]);
return 3;
}
@ -294,7 +363,7 @@ static int l_lovrColliderSetAngularVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float velocity[3];
luax_readvec3(L, 2, velocity, NULL);
lovrColliderSetAngularVelocity(collider, velocity[0], velocity[1], velocity[2]);
lovrColliderSetAngularVelocity(collider, velocity);
return 0;
}
@ -340,10 +409,9 @@ static int l_lovrColliderApplyForce(lua_State* L) {
if (lua_gettop(L) >= index) {
float position[3];
luax_readvec3(L, index, position, NULL);
lovrColliderApplyForceAtPosition(collider, force[0], force[1], force[2],
position[0], position[1], position[2]);
lovrColliderApplyForceAtPosition(collider, force, position);
} else {
lovrColliderApplyForce(collider, force[0], force[1], force[2]);
lovrColliderApplyForce(collider, force);
}
return 0;
@ -351,91 +419,107 @@ static int l_lovrColliderApplyForce(lua_State* L) {
static int l_lovrColliderApplyTorque(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float force[3];
luax_readvec3(L, 2, force, NULL);
lovrColliderApplyTorque(collider, force[0], force[1], force[2]);
float torque[3];
luax_readvec3(L, 2, torque, NULL);
lovrColliderApplyTorque(collider, torque);
return 0;
}
static int l_lovrColliderApplyLinearImpulse(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float impulse[3];
int index = luax_readvec3(L, 2, impulse, NULL);
if (lua_gettop(L) >= index) {
float position[3];
luax_readvec3(L, index, position, NULL);
lovrColliderApplyLinearImpulseAtPosition(collider, impulse, position);
} else {
lovrColliderApplyLinearImpulse(collider, impulse);
}
return 0;
}
static int l_lovrColliderApplyAngularImpulse(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float impulse[3];
luax_readvec3(L, 2, impulse, NULL);
lovrColliderApplyAngularImpulse(collider, impulse);
return 0;
}
static int l_lovrColliderGetLocalCenter(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float x, y, z;
lovrColliderGetLocalCenter(collider, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float center[3];
lovrColliderGetLocalCenter(collider, center);
lua_pushnumber(L, center[0]);
lua_pushnumber(L, center[1]);
lua_pushnumber(L, center[2]);
return 3;
}
static int l_lovrColliderGetLocalPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[3];
float world[3], local[3];
luax_readvec3(L, 2, world, NULL);
float x, y, z;
lovrColliderGetLocalPoint(collider, world[0], world[1], world[2], &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lovrColliderGetLocalPoint(collider, world, local);
lua_pushnumber(L, local[0]);
lua_pushnumber(L, local[1]);
lua_pushnumber(L, local[2]);
return 3;
}
static int l_lovrColliderGetWorldPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[3];
float local[3], world[3];
luax_readvec3(L, 2, local, NULL);
float wx, wy, wz;
lovrColliderGetWorldPoint(collider, local[0], local[1], local[2], &wx, &wy, &wz);
lua_pushnumber(L, wx);
lua_pushnumber(L, wy);
lua_pushnumber(L, wz);
lovrColliderGetWorldPoint(collider, local, world);
lua_pushnumber(L, world[0]);
lua_pushnumber(L, world[1]);
lua_pushnumber(L, world[2]);
return 3;
}
static int l_lovrColliderGetLocalVector(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[3];
float world[3], local[3];
luax_readvec3(L, 2, world, NULL);
float x, y, z;
lovrColliderGetLocalVector(collider, world[0], world[1], world[2], &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lovrColliderGetLocalVector(collider, world, local);
lua_pushnumber(L, local[0]);
lua_pushnumber(L, local[1]);
lua_pushnumber(L, local[2]);
return 3;
}
static int l_lovrColliderGetWorldVector(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[3];
float local[3], world[3];
luax_readvec3(L, 2, local, NULL);
float wx, wy, wz;
lovrColliderGetWorldVector(collider, local[0], local[1], local[2], &wx, &wy, &wz);
lua_pushnumber(L, wx);
lua_pushnumber(L, wy);
lua_pushnumber(L, wz);
lovrColliderGetWorldVector(collider, local, world);
lua_pushnumber(L, world[0]);
lua_pushnumber(L, world[1]);
lua_pushnumber(L, world[2]);
return 3;
}
static int l_lovrColliderGetLinearVelocityFromLocalPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[3];
luax_readvec3(L, 2, local, NULL);
float vx, vy, vz;
lovrColliderGetLinearVelocityFromLocalPoint(collider, local[0], local[1], local[2], &vx, &vy, &vz);
lua_pushnumber(L, vx);
lua_pushnumber(L, vy);
lua_pushnumber(L, vz);
float point[3], velocity[3];
luax_readvec3(L, 2, point, NULL);
lovrColliderGetLinearVelocityFromLocalPoint(collider, point, velocity);
lua_pushnumber(L, velocity[0]);
lua_pushnumber(L, velocity[1]);
lua_pushnumber(L, velocity[2]);
return 3;
}
static int l_lovrColliderGetLinearVelocityFromWorldPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[3];
luax_readvec3(L, 2, world, NULL);
float vx, vy, vz;
lovrColliderGetLinearVelocityFromWorldPoint(collider, world[0], world[1], world[2], &vx, &vy, &vz);
lua_pushnumber(L, vx);
lua_pushnumber(L, vy);
lua_pushnumber(L, vz);
float point[3], velocity[3];
luax_readvec3(L, 2, point, NULL);
lovrColliderGetLinearVelocityFromWorldPoint(collider, point, velocity);
lua_pushnumber(L, velocity[0]);
lua_pushnumber(L, velocity[1]);
lua_pushnumber(L, velocity[2]);
return 3;
}
@ -496,20 +580,52 @@ static int l_lovrColliderSetTag(lua_State* L) {
return 0;
}
// Deprecated
static int l_lovrColliderGetShapes(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = lovrColliderGetShape(collider, ~0u);
lua_createtable(L, 1, 0);
luax_pushshape(L, shape);
lua_rawseti(L, -2, 1);
return 1;
}
// Deprecated
static int l_lovrColliderIsGravityIgnored(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
lua_pushboolean(L, lovrColliderGetGravityScale(collider) == 0.f);
return 1;
}
// Deprecated
static int l_lovrColliderSetGravityIgnored(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool ignored = lua_toboolean(L, 2);
lovrColliderSetGravityScale(collider, ignored ? 0.f : 1.f);
return 0;
}
const luaL_Reg lovrCollider[] = {
{ "destroy", l_lovrColliderDestroy },
{ "isDestroyed", l_lovrColliderIsDestroyed },
{ "isEnabled", l_lovrColliderIsEnabled },
{ "setEnabled", l_lovrColliderSetEnabled },
{ "getWorld", l_lovrColliderGetWorld },
{ "addShape", l_lovrColliderAddShape },
{ "removeShape", l_lovrColliderRemoveShape },
{ "getShapes", l_lovrColliderGetShapes },
{ "getShape", l_lovrColliderGetShape },
{ "setShape", l_lovrColliderSetShape },
{ "getShapeOffset", l_lovrColliderGetShapeOffset },
{ "setShapeOffset", l_lovrColliderSetShapeOffset },
{ "getJoints", l_lovrColliderGetJoints },
{ "getUserData", l_lovrColliderGetUserData },
{ "setUserData", l_lovrColliderSetUserData },
{ "isKinematic", l_lovrColliderIsKinematic },
{ "setKinematic", l_lovrColliderSetKinematic },
{ "isGravityIgnored", l_lovrColliderIsGravityIgnored },
{ "setGravityIgnored", l_lovrColliderSetGravityIgnored },
{ "isSensor", l_lovrColliderIsSensor },
{ "setSensor", l_lovrColliderSetSensor },
{ "isContinuous", l_lovrColliderIsContinuous },
{ "setContinuous", l_lovrColliderSetContinuous },
{ "getGravityScale", l_lovrColliderGetGravityScale },
{ "setGravityScale", l_lovrColliderSetGravityScale },
{ "isSleepingAllowed", l_lovrColliderIsSleepingAllowed },
{ "setSleepingAllowed", l_lovrColliderSetSleepingAllowed },
{ "isAwake", l_lovrColliderIsAwake },
@ -534,6 +650,8 @@ const luaL_Reg lovrCollider[] = {
{ "setAngularDamping", l_lovrColliderSetAngularDamping },
{ "applyForce", l_lovrColliderApplyForce },
{ "applyTorque", l_lovrColliderApplyTorque },
{ "applyLinearImpulse", l_lovrColliderApplyLinearImpulse },
{ "applyAngularImpulse", l_lovrColliderApplyAngularImpulse },
{ "getLocalCenter", l_lovrColliderGetLocalCenter },
{ "getLocalPoint", l_lovrColliderGetLocalPoint },
{ "getWorldPoint", l_lovrColliderGetWorldPoint },
@ -548,5 +666,11 @@ const luaL_Reg lovrCollider[] = {
{ "setRestitution", l_lovrColliderSetRestitution },
{ "getTag", l_lovrColliderGetTag },
{ "setTag", l_lovrColliderSetTag },
// Deprecated
{ "getShapes", l_lovrColliderGetShapes },
{ "isGravityIgnored", l_lovrColliderIsGravityIgnored },
{ "setGravityIgnored", l_lovrColliderSetGravityIgnored },
{ NULL, NULL }
};

View File

@ -41,6 +41,13 @@ static int l_lovrJointDestroy(lua_State* L) {
return 0;
}
static int l_lovrJointIsDestroyed(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
bool destroyed = lovrJointIsDestroyed(joint);
lua_pushboolean(L, destroyed);
return 1;
}
static int l_lovrJointGetType(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
luax_pushenum(L, JointType, lovrJointGetType(joint));
@ -49,35 +56,45 @@ static int l_lovrJointGetType(lua_State* L) {
static int l_lovrJointGetColliders(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
Collider* a;
Collider* b;
lovrJointGetColliders(joint, &a, &b);
Collider* a = lovrJointGetColliderA(joint);
Collider* b = lovrJointGetColliderB(joint);
luax_pushtype(L, Collider, a);
luax_pushtype(L, Collider, b);
return 2;
}
static void luax_pushjointstash(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrjointstash");
if (lua_isnil(L, -1)) {
lua_newtable(L);
lua_replace(L, -2);
// metatable
lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrjointstash");
}
}
static int l_lovrJointGetUserData(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
union { int i; void* p; } ref = { .p = lovrJointGetUserData(joint) };
lua_rawgeti(L, LUA_REGISTRYINDEX, ref.i);
luax_checktype(L, 1, Joint);
luax_pushjointstash(L);
lua_pushvalue(L, 1);
lua_rawget(L, -2);
return 1;
}
static int l_lovrJointSetUserData(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
union { int i; void* p; } ref = { .p = lovrJointGetUserData(joint) };
if (ref.i) {
luaL_unref(L, LUA_REGISTRYINDEX, ref.i);
}
if (lua_gettop(L) < 2) {
lua_pushnil(L);
}
lua_settop(L, 2);
ref.i = luaL_ref(L, LUA_REGISTRYINDEX);
lovrJointSetUserData(joint, ref.p);
luax_checktype(L, 1, Joint);
luax_pushjointstash(L);
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_rawset(L, -3);
return 0;
}
@ -96,6 +113,7 @@ static int l_lovrJointSetEnabled(lua_State* L) {
#define lovrJoint \
{ "destroy", l_lovrJointDestroy }, \
{ "isDestroyed", l_lovrJointIsDestroyed }, \
{ "getType", l_lovrJointGetType }, \
{ "getColliders", l_lovrJointGetColliders }, \
{ "getUserData", l_lovrJointGetUserData }, \
@ -134,7 +152,7 @@ static int l_lovrBallJointGetResponseTime(lua_State* L) {
static int l_lovrBallJointSetResponseTime(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
float responseTime = luax_checkfloat(L, 2);
lovrAssert(responseTime >= 0, "Negative response time causes simulation instability");
lovrCheck(responseTime >= 0, "Negative response time causes simulation instability");
lovrBallJointSetResponseTime(joint, responseTime);
return 0;
}
@ -149,7 +167,7 @@ static int l_lovrBallJointGetTightness(lua_State* L) {
static int l_lovrBallJointSetTightness(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
float tightness = luax_checkfloat(L, 2);
lovrAssert(tightness >= 0, "Negative tightness factor causes simulation instability");
lovrCheck(tightness >= 0, "Negative tightness factor causes simulation instability");
lovrBallJointSetTightness(joint, tightness);
return 0;
}
@ -210,7 +228,7 @@ static int l_lovrDistanceJointGetResponseTime(lua_State* L) {
static int l_lovrDistanceJointSetResponseTime(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
float responseTime = luax_checkfloat(L, 2);
lovrAssert(responseTime >= 0, "Negative response time causes simulation instability");
lovrCheck(responseTime >= 0, "Negative response time causes simulation instability");
lovrDistanceJointSetResponseTime(joint, responseTime);
return 0;
}
@ -225,7 +243,7 @@ static int l_lovrDistanceJointGetTightness(lua_State* L) {
static int l_lovrDistanceJointSetTightness(lua_State* L) {
Joint* joint = luax_checkjoint(L, 1);
float tightness = luax_checkfloat(L, 2);
lovrAssert(tightness >= 0, "Negative tightness factor causes simulation instability");
lovrCheck(tightness >= 0, "Negative tightness factor causes simulation instability");
lovrDistanceJointSetTightness(joint, tightness);
return 0;
}

View File

@ -12,8 +12,10 @@ void luax_pushshape(lua_State* L, Shape* shape) {
case SHAPE_BOX: luax_pushtype(L, BoxShape, shape); break;
case SHAPE_CAPSULE: luax_pushtype(L, CapsuleShape, shape); break;
case SHAPE_CYLINDER: luax_pushtype(L, CylinderShape, shape); break;
case SHAPE_CONVEX: luax_pushtype(L, ConvexShape, shape); break;
case SHAPE_MESH: luax_pushtype(L, MeshShape, shape); break;
case SHAPE_TERRAIN: luax_pushtype(L, TerrainShape, shape); break;
case SHAPE_COMPOUND: luax_pushtype(L, CompoundShape, shape); break;
default: lovrUnreachable();
}
}
@ -27,8 +29,10 @@ Shape* luax_checkshape(lua_State* L, int index) {
hash64("BoxShape", strlen("BoxShape")),
hash64("CapsuleShape", strlen("CapsuleShape")),
hash64("CylinderShape", strlen("CylinderShape")),
hash64("ConvexShape", strlen("ConvexShape")),
hash64("MeshShape", strlen("MeshShape")),
hash64("TerrainShape", strlen("TerrainShape"))
hash64("TerrainShape", strlen("TerrainShape")),
hash64("CompoundShape", strlen("CompoundShape"))
};
for (size_t i = 0; i < COUNTOF(hashes); i++) {
@ -50,7 +54,7 @@ Shape* luax_newsphereshape(lua_State* L, int index) {
Shape* luax_newboxshape(lua_State* L, int index) {
float size[3];
luax_readscale(L, index, size, 3, NULL);
return lovrBoxShapeCreate(size[0], size[1], size[2]);
return lovrBoxShapeCreate(size);
}
Shape* luax_newcapsuleshape(lua_State* L, int index) {
@ -65,6 +69,16 @@ Shape* luax_newcylindershape(lua_State* L, int index) {
return lovrCylinderShapeCreate(radius, length);
}
Shape* luax_newconvexshape(lua_State* L, int index) {
float* points;
uint32_t count;
bool shouldFree;
luax_readmesh(L, index, &points, &count, NULL, NULL, &shouldFree);
ConvexShape* shape = lovrConvexShapeCreate(points, count);
if (shouldFree) lovrFree(points);
return shape;
}
Shape* luax_newmeshshape(lua_State* L, int index) {
float* vertices;
uint32_t* indices;
@ -81,9 +95,8 @@ Shape* luax_newmeshshape(lua_State* L, int index) {
if (!shouldFree) {
float* v = vertices;
uint32_t* i = indices;
vertices = malloc(3 * vertexCount * sizeof(float));
indices = malloc(indexCount * sizeof(uint32_t));
lovrAssert(vertices && indices, "Out of memory");
vertices = lovrMalloc(3 * vertexCount * sizeof(float));
indices = lovrMalloc(indexCount * sizeof(uint32_t));
memcpy(vertices, v, 3 * vertexCount * sizeof(float));
memcpy(indices, i, indexCount * sizeof(uint32_t));
}
@ -92,18 +105,17 @@ Shape* luax_newmeshshape(lua_State* L, int index) {
}
Shape* luax_newterrainshape(lua_State* L, int index) {
float horizontalScale = luax_checkfloat(L, index++);
float scaleXZ = luax_checkfloat(L, index++);
int type = lua_type(L, index);
if (type == LUA_TNIL || type == LUA_TNONE) {
float vertices[4] = { 0.f };
return lovrTerrainShapeCreate(vertices, 2, 2, horizontalScale, 1.f);
return lovrTerrainShapeCreate(vertices, 2, scaleXZ, 1.f);
} else if (type == LUA_TFUNCTION) {
uint32_t samples = luax_optu32(L, index + 1, 100);
float* vertices = malloc(sizeof(float) * samples * samples);
lovrAssert(vertices, "Out of memory");
for (uint32_t i = 0; i < samples * samples; i++) {
float x = horizontalScale * (-.5f + ((float) (i % samples)) / samples);
float z = horizontalScale * (-.5f + ((float) (i / samples)) / samples);
uint32_t n = luax_optu32(L, index + 1, 100);
float* vertices = lovrMalloc(sizeof(float) * n * n);
for (uint32_t i = 0; i < n * n; i++) {
float x = scaleXZ * (-.5f + ((float) (i % n)) / n);
float z = scaleXZ * (-.5f + ((float) (i / n)) / n);
lua_pushvalue(L, index);
lua_pushnumber(L, x);
lua_pushnumber(L, z);
@ -112,25 +124,24 @@ Shape* luax_newterrainshape(lua_State* L, int index) {
vertices[i] = luax_tofloat(L, -1);
lua_pop(L, 1);
}
TerrainShape* shape = lovrTerrainShapeCreate(vertices, samples, samples, horizontalScale, 1.f);
free(vertices);
TerrainShape* shape = lovrTerrainShapeCreate(vertices, n, scaleXZ, 1.f);
lovrFree(vertices);
return shape;
} else if (type == LUA_TUSERDATA) {
Image* image = luax_checktype(L, index, Image);
uint32_t imageWidth = lovrImageGetWidth(image, 0);
uint32_t imageHeight = lovrImageGetHeight(image, 0);
float verticalScale = luax_optfloat(L, index + 1, 1.f);
float* vertices = malloc(sizeof(float) * imageWidth * imageHeight);
lovrAssert(vertices, "Out of memory");
for (uint32_t y = 0; y < imageHeight; y++) {
for (uint32_t x = 0; x < imageWidth; x++) {
uint32_t n = lovrImageGetWidth(image, 0);
lovrCheck(lovrImageGetHeight(image, 0) == n, "TerrainShape images must be square");
float scaleY = luax_optfloat(L, index + 1, 1.f);
float* vertices = lovrMalloc(sizeof(float) * n * n);
for (uint32_t y = 0; y < n; y++) {
for (uint32_t x = 0; x < n; x++) {
float pixel[4];
lovrImageGetPixel(image, x, y, pixel);
vertices[x + y * imageWidth] = pixel[0];
vertices[x + y * n] = pixel[0];
}
}
TerrainShape* shape = lovrTerrainShapeCreate(vertices, imageWidth, imageHeight, horizontalScale, verticalScale);
free(vertices);
TerrainShape* shape = lovrTerrainShapeCreate(vertices, n, scaleXZ, scaleY);
lovrFree(vertices);
return shape;
} else {
luax_typeerror(L, index, "Image, number, or function");
@ -138,6 +149,85 @@ Shape* luax_newterrainshape(lua_State* L, int index) {
}
}
Shape* luax_newcompoundshape(lua_State* L, int index) {
if (lua_isnoneornil(L, index)) {
return lovrCompoundShapeCreate(NULL, NULL, NULL, 0, false);
}
luaL_checktype(L, index, LUA_TTABLE);
int length = luax_len(L, index);
uint32_t defer = lovrDeferPush();
Shape** shapes = lovrMalloc(length * sizeof(Shape*));
float* positions = lovrMalloc(length * 3 * sizeof(float));
float* orientations = lovrMalloc(length * 4 * sizeof(float));
lovrDefer(lovrFree, shapes);
lovrDefer(lovrFree, positions);
lovrDefer(lovrFree, orientations);
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
lovrCheck(lua_istable(L, -1), "Expected table of tables for compound shape");
lua_rawgeti(L, -1, 1);
shapes[i] = luax_checkshape(L, -1);
lua_pop(L, 1);
int index = 2;
lua_rawgeti(L, -1, index);
switch (lua_type(L, -1)) {
case LUA_TNIL:
vec3_set(&positions[3 * i], 0.f, 0.f, 0.f);
lua_pop(L, 1);
break;
case LUA_TNUMBER:
lua_rawgeti(L, -2, index + 1);
lua_rawgeti(L, -3, index + 2);
vec3_set(&positions[3 * i], luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1));
lua_pop(L, 3);
index += 3;
break;
default: {
float* v = luax_checkvector(L, -1, V_VEC3, "nil, number, or vec3");
vec3_init(&positions[3 * i], v);
lua_pop(L, 1);
break;
}
}
lua_rawgeti(L, -1, index);
switch (lua_type(L, -1)) {
case LUA_TNIL:
quat_identity(&orientations[4 * i]);
lua_pop(L, 1);
break;
case LUA_TNUMBER:
lua_rawgeti(L, -2, index);
lua_rawgeti(L, -3, index);
lua_rawgeti(L, -4, index);
quat_set(&orientations[4 * i], luax_tofloat(L, -4), luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1));
lua_pop(L, 4);
break;
default: {
float* q = luax_checkvector(L, -1, V_QUAT, "nil, number, or quat");
quat_init(&positions[4 * i], q);
lua_pop(L, 1);
break;
}
}
lua_pop(L, 1);
}
lua_getfield(L, index, "freeze");
bool freeze = lua_toboolean(L, -1);
lua_pop(L, 1);
CompoundShape* shape = lovrCompoundShapeCreate(shapes, positions, orientations, length, freeze);
lovrDeferPop(defer);
return shape;
}
static int l_lovrShapeDestroy(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrShapeDestroyData(shape);
@ -150,139 +240,49 @@ static int l_lovrShapeGetType(lua_State* L) {
return 1;
}
static int l_lovrShapeGetCollider(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
luax_pushtype(L, Collider, lovrShapeGetCollider(shape));
return 1;
}
static void luax_pushshapestash(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrshapestash");
static int l_lovrShapeIsEnabled(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lua_pushboolean(L, lovrShapeIsEnabled(shape));
return 1;
}
if (lua_isnil(L, -1)) {
lua_newtable(L);
lua_replace(L, -2);
static int l_lovrShapeSetEnabled(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
bool enabled = lua_toboolean(L, 2);
lovrShapeSetEnabled(shape, enabled);
return 0;
}
// metatable
lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
static int l_lovrShapeIsSensor(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lua_pushboolean(L, lovrShapeIsSensor(shape));
return 1;
}
static int l_lovrShapeSetSensor(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
bool sensor = lua_toboolean(L, 2);
lovrShapeSetSensor(shape, sensor);
return 0;
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrshapestash");
}
}
static int l_lovrShapeGetUserData(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
union { int i; void* p; } ref = { .p = lovrShapeGetUserData(shape) };
lua_rawgeti(L, LUA_REGISTRYINDEX, ref.i);
luax_checktype(L, 1, Shape);
luax_pushshapestash(L);
lua_pushvalue(L, 1);
lua_rawget(L, -2);
return 1;
}
static int l_lovrShapeSetUserData(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
union { int i; void* p; } ref = { .p = lovrShapeGetUserData(shape) };
if (ref.i) {
luaL_unref(L, LUA_REGISTRYINDEX, ref.i);
}
if (lua_gettop(L) < 2) {
lua_pushnil(L);
}
lua_settop(L, 2);
ref.i = luaL_ref(L, LUA_REGISTRYINDEX);
lovrShapeSetUserData(shape, ref.p);
return 0;
}
static int l_lovrShapeGetPosition(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
static int l_lovrShapeSetPosition(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrAssert(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
return 0;
}
static int l_lovrShapeGetOrientation(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float angle, x, y, z, orientation[4];
lovrShapeGetOrientation(shape, orientation);
quat_getAngleAxis(orientation, &angle, &x, &y, &z);
lua_pushnumber(L, angle);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 4;
}
static int l_lovrShapeSetOrientation(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrAssert(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float orientation[4];
luax_readquat(L, 2, orientation, NULL);
lovrShapeSetOrientation(shape, orientation);
return 0;
}
static int l_lovrShapeGetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z);
float angle, ax, ay, az, orientation[4];
lovrShapeGetOrientation(shape, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrShapeSetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrAssert(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[3], orientation[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readquat(L, index, orientation, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
lovrShapeSetOrientation(shape, orientation);
luax_checktype(L, 1, Shape);
luax_pushshapestash(L);
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_rawset(L, -3);
return 0;
}
static int l_lovrShapeGetMass(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float density = luax_checkfloat(L, 2);
float cx, cy, cz, mass;
float inertia[6];
lovrShapeGetMass(shape, density, &cx, &cy, &cz, &mass, inertia);
lua_pushnumber(L, cx);
lua_pushnumber(L, cy);
lua_pushnumber(L, cz);
float centerOfMass[3], mass, inertia[6];
lovrShapeGetMass(shape, density, centerOfMass, &mass, inertia);
lua_pushnumber(L, centerOfMass[0]);
lua_pushnumber(L, centerOfMass[1]);
lua_pushnumber(L, centerOfMass[2]);
lua_pushnumber(L, mass);
lua_newtable(L);
for (int i = 0; i < 6; i++) {
@ -294,8 +294,15 @@ static int l_lovrShapeGetMass(lua_State* L) {
static int l_lovrShapeGetAABB(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float aabb[6];
lovrShapeGetAABB(shape, aabb);
float position[3], orientation[4], aabb[6];
if (lua_gettop(L) >= 2) {
int index = 2;
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, orientation, NULL);
lovrShapeGetAABB(shape, position, orientation, aabb);
} else {
lovrShapeGetAABB(shape, NULL, NULL, aabb);
}
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, aabb[i]);
}
@ -305,19 +312,8 @@ static int l_lovrShapeGetAABB(lua_State* L) {
#define lovrShape \
{ "destroy", l_lovrShapeDestroy }, \
{ "getType", l_lovrShapeGetType }, \
{ "getCollider", l_lovrShapeGetCollider }, \
{ "isEnabled", l_lovrShapeIsEnabled }, \
{ "setEnabled", l_lovrShapeSetEnabled }, \
{ "isSensor", l_lovrShapeIsSensor }, \
{ "setSensor", l_lovrShapeSetSensor }, \
{ "getUserData", l_lovrShapeGetUserData }, \
{ "setUserData", l_lovrShapeSetUserData }, \
{ "getPosition", l_lovrShapeGetPosition }, \
{ "setPosition", l_lovrShapeSetPosition }, \
{ "getOrientation", l_lovrShapeGetOrientation }, \
{ "setOrientation", l_lovrShapeSetOrientation }, \
{ "getPose", l_lovrShapeGetPose }, \
{ "setPose", l_lovrShapeSetPose }, \
{ "getMass", l_lovrShapeGetMass }, \
{ "getAABB", l_lovrShapeGetAABB }
@ -327,42 +323,25 @@ static int l_lovrSphereShapeGetRadius(lua_State* L) {
return 1;
}
static int l_lovrSphereShapeSetRadius(lua_State* L) {
SphereShape* sphere = luax_checktype(L, 1, SphereShape);
float radius = luax_checkfloat(L, 2);
lovrSphereShapeSetRadius(sphere, radius);
return 0;
}
const luaL_Reg lovrSphereShape[] = {
lovrShape,
{ "getRadius", l_lovrSphereShapeGetRadius },
{ "setRadius", l_lovrSphereShapeSetRadius },
{ NULL, NULL }
};
static int l_lovrBoxShapeGetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float w, h, d;
lovrBoxShapeGetDimensions(box, &w, &h, &d);
lua_pushnumber(L, w);
lua_pushnumber(L, h);
lua_pushnumber(L, d);
float dimensions[3];
lovrBoxShapeGetDimensions(box, dimensions);
lua_pushnumber(L, dimensions[0]);
lua_pushnumber(L, dimensions[1]);
lua_pushnumber(L, dimensions[2]);
return 3;
}
static int l_lovrBoxShapeSetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float size[3];
luax_readscale(L, 2, size, 3, NULL);
lovrBoxShapeSetDimensions(box, size[0], size[1], size[2]);
return 0;
}
const luaL_Reg lovrBoxShape[] = {
lovrShape,
{ "getDimensions", l_lovrBoxShapeGetDimensions },
{ "setDimensions", l_lovrBoxShapeSetDimensions },
{ NULL, NULL }
};
@ -372,32 +351,16 @@ static int l_lovrCapsuleShapeGetRadius(lua_State* L) {
return 1;
}
static int l_lovrCapsuleShapeSetRadius(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
float radius = luax_checkfloat(L, 2);
lovrCapsuleShapeSetRadius(capsule, radius);
return 0;
}
static int l_lovrCapsuleShapeGetLength(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
lua_pushnumber(L, lovrCapsuleShapeGetLength(capsule));
return 1;
}
static int l_lovrCapsuleShapeSetLength(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
float length = luax_checkfloat(L, 2);
lovrCapsuleShapeSetLength(capsule, length);
return 0;
}
const luaL_Reg lovrCapsuleShape[] = {
lovrShape,
{ "getRadius", l_lovrCapsuleShapeGetRadius },
{ "setRadius", l_lovrCapsuleShapeSetRadius },
{ "getLength", l_lovrCapsuleShapeGetLength },
{ "setLength", l_lovrCapsuleShapeSetLength },
{ NULL, NULL }
};
@ -407,32 +370,21 @@ static int l_lovrCylinderShapeGetRadius(lua_State* L) {
return 1;
}
static int l_lovrCylinderShapeSetRadius(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
float radius = luax_checkfloat(L, 2);
lovrCylinderShapeSetRadius(cylinder, radius);
return 0;
}
static int l_lovrCylinderShapeGetLength(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
lua_pushnumber(L, lovrCylinderShapeGetLength(cylinder));
return 1;
}
static int l_lovrCylinderShapeSetLength(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
float length = luax_checkfloat(L, 2);
lovrCylinderShapeSetLength(cylinder, length);
return 0;
}
const luaL_Reg lovrCylinderShape[] = {
lovrShape,
{ "getRadius", l_lovrCylinderShapeGetRadius },
{ "setRadius", l_lovrCylinderShapeSetRadius },
{ "getLength", l_lovrCylinderShapeGetLength },
{ "setLength", l_lovrCylinderShapeSetLength },
{ NULL, NULL }
};
const luaL_Reg lovrConvexShape[] = {
lovrShape,
{ NULL, NULL }
};
@ -445,3 +397,109 @@ const luaL_Reg lovrTerrainShape[] = {
lovrShape,
{ NULL, NULL }
};
static int l_lovrCompoundShapeIsFrozen(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
bool frozen = lovrCompoundShapeIsFrozen(shape);
lua_pushboolean(L, frozen);
return 1;
}
static int l_lovrCompoundShapeAddChild(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
Shape* child = luax_checkshape(L, 2);
float position[3], orientation[4];
int index = 3;
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, orientation, NULL);
lovrCompoundShapeAddChild(shape, child, position, orientation);
return 0;
}
static int l_lovrCompoundShapeReplaceChild(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t index = luax_checku32(L, 2) - 1;
Shape* child = luax_checkshape(L, 3);
float position[3], orientation[4];
int i = 4;
i = luax_readvec3(L, i, position, NULL);
i = luax_readquat(L, i, orientation, NULL);
lovrCompoundShapeReplaceChild(shape, index, child, position, orientation);
return 0;
}
static int l_lovrCompoundShapeRemoveChild(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t index = luax_checku32(L, 2) - 1;
lovrCompoundShapeRemoveChild(shape, index);
return 0;
}
static int l_lovrCompoundShapeGetChild(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t index = luax_checku32(L, 2) - 1;
Shape* child = lovrCompoundShapeGetChild(shape, index);
luax_pushshape(L, child);
return 1;
}
static int l_lovrCompoundShapeGetChildren(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
int count = (int) lovrCompoundShapeGetChildCount(shape);
lua_createtable(L, count, 0);
for (int i = 0; i < count; i++) {
Shape* child = lovrCompoundShapeGetChild(shape, (uint32_t) i);
luax_pushshape(L, child);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int l_lovrCompoundShapeGetChildCount(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t count = lovrCompoundShapeGetChildCount(shape);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrCompoundShapeGetChildOffset(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t index = luax_checku32(L, 2) - 1;
float position[3], orientation[4], angle, ax, ay, az;
lovrCompoundShapeGetChildOffset(shape, index, position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrCompoundShapeSetChildOffset(lua_State* L) {
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
uint32_t index = luax_checku32(L, 2) - 1;
float position[3], orientation[4];
int i = 3;
i = luax_readvec3(L, i, position, NULL);
i = luax_readquat(L, i, orientation, NULL);
lovrCompoundShapeSetChildOffset(shape, index, position, orientation);
return 0;
}
const luaL_Reg lovrCompoundShape[] = {
lovrShape,
{ "isFrozen", l_lovrCompoundShapeIsFrozen },
{ "addChild", l_lovrCompoundShapeAddChild },
{ "replaceChild", l_lovrCompoundShapeReplaceChild },
{ "removeChild", l_lovrCompoundShapeRemoveChild },
{ "getChild", l_lovrCompoundShapeGetChild },
{ "getChildren", l_lovrCompoundShapeGetChildren },
{ "getChildCount", l_lovrCompoundShapeGetChildCount },
{ "getChildOffset", l_lovrCompoundShapeGetChildOffset },
{ "setChildOffset", l_lovrCompoundShapeSetChildOffset },
{ "__len", l_lovrCompoundShapeGetChildCount }, // :)
{ NULL, NULL }
};

View File

@ -27,17 +27,18 @@ static int nextOverlap(lua_State* L) {
}
}
static bool raycastCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) {
static bool raycastCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) {
lua_State* L = userdata;
lua_pushvalue(L, -1);
luax_pushshape(L, shape);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lua_pushnumber(L, nx);
lua_pushnumber(L, ny);
lua_pushnumber(L, nz);
lua_call(L, 7, 1);
luax_pushtype(L, Collider, collider);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
lua_pushnumber(L, normal[0]);
lua_pushnumber(L, normal[1]);
lua_pushnumber(L, normal[2]);
lua_pushinteger(L, shape + 1);
lua_call(L, 8, 1);
bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1);
lua_pop(L, 1);
return shouldStop;
@ -45,53 +46,55 @@ static bool raycastCallback(Shape* shape, float x, float y, float z, float nx, f
typedef struct {
const char* tag;
Shape* shape;
Collider* collider;
uint32_t shape;
float distance;
float origin[3];
float position[3];
float normal[3];
} RaycastData;
static bool raycastAnyCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) {
static bool raycastAnyCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) {
RaycastData* data = userdata;
if (data->tag) {
const char* tag = lovrColliderGetTag(lovrShapeGetCollider(shape));
const char* tag = lovrColliderGetTag(collider);
if (!tag || strcmp(tag, data->tag)) {
return false;
}
}
data->collider = collider;
data->shape = shape;
vec3_set(data->position, x, y, z);
vec3_set(data->normal, nx, ny, nz);
vec3_init(data->position, position);
vec3_init(data->normal, normal);
data->distance = vec3_distance(data->origin, data->position);
return true;
}
static bool raycastClosestCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) {
static bool raycastClosestCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) {
RaycastData* data = userdata;
if (data->tag) {
const char* tag = lovrColliderGetTag(lovrShapeGetCollider(shape));
const char* tag = lovrColliderGetTag(collider);
if (!tag || strcmp(tag, data->tag)) {
return false;
}
}
float position[3];
vec3_set(position, x, y, z);
float distance = vec3_distance(data->origin, position);
if (distance < data->distance) {
vec3_init(data->position, position);
vec3_set(data->normal, nx, ny, nz);
vec3_init(data->normal, normal);
data->distance = distance;
data->collider = collider;
data->shape = shape;
}
return false;
}
static bool queryCallback(Shape* shape, void* userdata) {
static bool queryCallback(Collider* collider, uint32_t shape, void* userdata) {
lua_State* L = userdata;
lua_pushvalue(L, -1);
luax_pushshape(L, shape);
lua_call(L, 1, 1);
luax_pushtype(L, Collider, collider);
lua_pushinteger(L, shape + 1);
lua_call(L, 2, 1);
bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1);
lua_pop(L, 1);
return shouldStop;
@ -99,9 +102,10 @@ static bool queryCallback(Shape* shape, void* userdata) {
static int l_lovrWorldNewCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
Shape* shape = luax_totype(L, 2, Shape);
float position[3];
luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
luax_readvec3(L, 2 + !!shape, position, NULL);
Collider* collider = lovrColliderCreate(world, shape, position);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
return 1;
@ -111,9 +115,8 @@ static int l_lovrWorldNewBoxCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
BoxShape* shape = luax_newboxshape(L, index);
lovrColliderAddShape(collider, shape);
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -125,9 +128,8 @@ static int l_lovrWorldNewCapsuleCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CapsuleShape* shape = luax_newcapsuleshape(L, index);
lovrColliderAddShape(collider, shape);
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -139,9 +141,21 @@ static int l_lovrWorldNewCylinderCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CylinderShape* shape = luax_newcylindershape(L, index);
lovrColliderAddShape(collider, shape);
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
lovrRelease(shape, lovrShapeDestroy);
return 1;
}
static int l_lovrWorldNewConvexCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
ConvexShape* shape = luax_newconvexshape(L, index);
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -153,9 +167,8 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
SphereShape* shape = luax_newsphereshape(L, index);
lovrColliderAddShape(collider, shape);
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -165,9 +178,9 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) {
static int l_lovrWorldNewMeshCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f);
MeshShape* shape = luax_newmeshshape(L, 2);
lovrColliderAddShape(collider, shape);
float position[3] = { 0.f, 0.f, 0.f };
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -177,9 +190,9 @@ static int l_lovrWorldNewMeshCollider(lua_State* L) {
static int l_lovrWorldNewTerrainCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f);
TerrainShape* shape = luax_newterrainshape(L, 2);
lovrColliderAddShape(collider, shape);
float position[3] = { 0.f, 0.f, 0.f };
Collider* collider = lovrColliderCreate(world, shape, position);
lovrColliderSetKinematic(collider, true);
luax_pushtype(L, Collider, collider);
lovrRelease(collider, lovrColliderDestroy);
@ -187,25 +200,10 @@ static int l_lovrWorldNewTerrainCollider(lua_State* L) {
return 1;
}
static int l_lovrWorldGetColliders(lua_State* L) {
static int l_lovrWorldDestroy(lua_State* L) {
World* world = luax_checktype(L, 1, World);
if (lua_istable(L, 2)) {
lua_settop(L, 2);
} else {
lua_newtable(L);
}
Collider* collider = lovrWorldGetFirstCollider(world);
int index = 1;
while (collider) {
luax_pushtype(L, Collider, collider);
lua_rawseti(L, -2, index++);
collider = lovrColliderGetNext(collider);
}
return 1;
lovrWorldDestroyData(world);
return 0;
}
static int l_lovrWorldGetTags(lua_State* L) {
@ -221,10 +219,42 @@ static int l_lovrWorldGetTags(lua_State* L) {
return 1;
}
static int l_lovrWorldDestroy(lua_State* L) {
static int l_lovrWorldGetColliderCount(lua_State* L) {
World* world = luax_checktype(L, 1, World);
lovrWorldDestroyData(world);
return 0;
uint32_t count = lovrWorldGetColliderCount(world);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrWorldGetJointCount(lua_State* L) {
World* world = luax_checktype(L, 1, World);
uint32_t count = lovrWorldGetJointCount(world);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrWorldGetColliders(lua_State* L) {
World* world = luax_checktype(L, 1, World);
int index = 1;
Collider* collider = NULL;
lua_createtable(L, (int) lovrWorldGetColliderCount(world), 0);
while ((collider = lovrWorldGetColliders(world, collider)) != NULL) {
luax_pushtype(L, Collider, collider);
lua_rawseti(L, -2, index++);
}
return 1;
}
static int l_lovrWorldGetJoints(lua_State* L) {
World* world = luax_checktype(L, 1, World);
int index = 1;
Joint* joint = NULL;
lua_createtable(L, (int) lovrWorldGetJointCount(world), 0);
while ((joint = lovrWorldGetJoints(world, joint)) != NULL) {
luax_pushjoint(L, joint);
lua_rawseti(L, -2, index++);
}
return 1;
}
static int l_lovrWorldUpdate(lua_State* L) {
@ -296,7 +326,7 @@ static int l_lovrWorldRaycast(lua_State* L) {
index = luax_readvec3(L, index, end, NULL);
luaL_checktype(L, index, LUA_TFUNCTION);
lua_settop(L, index);
lovrWorldRaycast(world, start[0], start[1], start[2], end[0], end[1], end[2], raycastCallback, L);
lovrWorldRaycast(world, start, end, raycastCallback, L);
return 0;
}
@ -308,16 +338,17 @@ static int l_lovrWorldRaycastAny(lua_State* L) {
index = luax_readvec3(L, index, end, NULL);
RaycastData data = { 0 };
data.tag = lua_tostring(L, index);
lovrWorldRaycast(world, start[0], start[1], start[2], end[0], end[1], end[2], raycastAnyCallback, &data);
if (data.shape) {
luax_pushshape(L, data.shape);
lovrWorldRaycast(world, start, end, raycastAnyCallback, &data);
if (data.collider) {
luax_pushtype(L, Collider, data.collider);
lua_pushnumber(L, data.position[0]);
lua_pushnumber(L, data.position[1]);
lua_pushnumber(L, data.position[2]);
lua_pushnumber(L, data.normal[0]);
lua_pushnumber(L, data.normal[1]);
lua_pushnumber(L, data.normal[2]);
return 7;
lua_pushinteger(L, data.shape + 1);
return 8;
} else {
lua_pushnil(L);
return 1;
@ -332,16 +363,17 @@ static int l_lovrWorldRaycastClosest(lua_State* L) {
index = luax_readvec3(L, index, end, NULL);
RaycastData data = { .distance = FLT_MAX };
data.tag = lua_tostring(L, index);
lovrWorldRaycast(world, start[0], start[1], start[2], end[0], end[1], end[2], raycastClosestCallback, &data);
lovrWorldRaycast(world, start, end, raycastClosestCallback, &data);
if (data.shape) {
luax_pushshape(L, data.shape);
luax_pushtype(L, Collider, data.collider);
lua_pushnumber(L, data.position[0]);
lua_pushnumber(L, data.position[1]);
lua_pushnumber(L, data.position[2]);
lua_pushnumber(L, data.normal[0]);
lua_pushnumber(L, data.normal[1]);
lua_pushnumber(L, data.normal[2]);
return 7;
lua_pushinteger(L, data.shape + 1);
return 8;
} else {
lua_pushnil(L);
return 1;
@ -375,11 +407,11 @@ static int l_lovrWorldQuerySphere(lua_State* L) {
static int l_lovrWorldGetGravity(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float x, y, z;
lovrWorldGetGravity(world, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float gravity[3];
lovrWorldGetGravity(world, gravity);
lua_pushnumber(L, gravity[0]);
lua_pushnumber(L, gravity[1]);
lua_pushnumber(L, gravity[2]);
return 3;
}
@ -387,14 +419,14 @@ static int l_lovrWorldSetGravity(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float gravity[3];
luax_readvec3(L, 2, gravity, NULL);
lovrWorldSetGravity(world, gravity[0], gravity[1], gravity[2]);
lovrWorldSetGravity(world, gravity);
return 0;
}
static int l_lovrWorldGetTightness(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float tightness = lovrWorldGetTightness(world);
lovrAssert(tightness >= 0, "Negative tightness factor causes simulation instability");
lovrCheck(tightness >= 0, "Negative tightness factor causes simulation instability");
lua_pushnumber(L, tightness);
return 1;
}
@ -416,7 +448,7 @@ static int l_lovrWorldGetResponseTime(lua_State* L) {
static int l_lovrWorldSetResponseTime(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float responseTime = luax_checkfloat(L, 2);
lovrAssert(responseTime >= 0, "Negative response time causes simulation instability");
lovrCheck(responseTime >= 0, "Negative response time causes simulation instability");
lovrWorldSetResponseTime(world, responseTime);
return 0;
}
@ -511,12 +543,16 @@ const luaL_Reg lovrWorld[] = {
{ "newBoxCollider", l_lovrWorldNewBoxCollider },
{ "newCapsuleCollider", l_lovrWorldNewCapsuleCollider },
{ "newCylinderCollider", l_lovrWorldNewCylinderCollider },
{ "newConvexCollider", l_lovrWorldNewConvexCollider },
{ "newSphereCollider", l_lovrWorldNewSphereCollider },
{ "newMeshCollider", l_lovrWorldNewMeshCollider },
{ "newTerrainCollider", l_lovrWorldNewTerrainCollider },
{ "getColliders", l_lovrWorldGetColliders },
{ "getTags", l_lovrWorldGetTags },
{ "destroy", l_lovrWorldDestroy },
{ "getTags", l_lovrWorldGetTags },
{ "getColliderCount", l_lovrWorldGetColliderCount },
{ "getJointCount", l_lovrWorldGetJointCount },
{ "getColliders", l_lovrWorldGetColliders },
{ "getJoints", l_lovrWorldGetJoints },
{ "update", l_lovrWorldUpdate },
{ "computeOverlaps", l_lovrWorldComputeOverlaps },
{ "overlaps", l_lovrWorldOverlaps },
@ -529,6 +565,11 @@ const luaL_Reg lovrWorld[] = {
{ "querySphere", l_lovrWorldQuerySphere },
{ "getGravity", l_lovrWorldGetGravity },
{ "setGravity", l_lovrWorldSetGravity },
{ "disableCollisionBetween", l_lovrWorldDisableCollisionBetween },
{ "enableCollisionBetween", l_lovrWorldEnableCollisionBetween },
{ "isCollisionEnabledBetween", l_lovrWorldIsCollisionEnabledBetween },
// Deprecated
{ "getTightness", l_lovrWorldGetTightness },
{ "setTightness", l_lovrWorldSetTightness },
{ "getResponseTime", l_lovrWorldGetResponseTime },
@ -539,10 +580,8 @@ const luaL_Reg lovrWorld[] = {
{ "setAngularDamping", l_lovrWorldSetAngularDamping },
{ "isSleepingAllowed", l_lovrWorldIsSleepingAllowed },
{ "setSleepingAllowed", l_lovrWorldSetSleepingAllowed },
{ "disableCollisionBetween", l_lovrWorldDisableCollisionBetween },
{ "enableCollisionBetween", l_lovrWorldEnableCollisionBetween },
{ "isCollisionEnabledBetween", l_lovrWorldIsCollisionEnabledBetween },
{ "getStepCount", l_lovrWorldGetStepCount },
{ "setStepCount", l_lovrWorldSetStepCount },
{ NULL, NULL }
};

View File

@ -141,6 +141,8 @@ static int l_lovrSystemOpenWindow(lua_State* L) {
os_window_config window;
memset(&window, 0, sizeof(window));
uint32_t defer = lovrDeferPush();
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "width");
@ -170,11 +172,12 @@ static int l_lovrSystemOpenWindow(lua_State* L) {
window.icon.data = lovrImageGetLayerData(image, 0, 0);
window.icon.width = lovrImageGetWidth(image, 0);
window.icon.height = lovrImageGetHeight(image, 0);
lovrDeferRelease(image, lovrImageDestroy);
}
lua_pop(L, 1);
lovrSystemOpenWindow(&window);
lovrRelease(image, lovrImageDestroy);
lovrDeferPop(defer);
return 0;
}
@ -309,6 +312,18 @@ static int l_lovrSystemWasMouseReleased(lua_State* L) {
return 1;
}
static int l_lovrSystemGetClipboardText(lua_State* L) {
const char* text = lovrSystemGetClipboardText();
lua_pushstring(L, text);
return 1;
}
static int l_lovrSystemSetClipboardText(lua_State* L) {
const char* text = luaL_checkstring(L, 1);
lovrSystemSetClipboardText(text);
return 0;
}
static const luaL_Reg lovrSystem[] = {
{ "getOS", l_lovrSystemGetOS },
{ "getCoreCount", l_lovrSystemGetCoreCount },
@ -332,6 +347,8 @@ static const luaL_Reg lovrSystem[] = {
{ "isMouseDown", l_lovrSystemIsMouseDown },
{ "wasMousePressed", l_lovrSystemWasMousePressed },
{ "wasMouseReleased", l_lovrSystemWasMouseReleased },
{ "getClipboardText", l_lovrSystemGetClipboardText },
{ "setClipboardText", l_lovrSystemSetClipboardText },
{ NULL, NULL }
};

View File

@ -2,36 +2,45 @@
#include "data/blob.h"
#include "event/event.h"
#include "thread/thread.h"
#include "core/os.h"
#include "util.h"
#include <lualib.h>
#include <stdlib.h>
#include <string.h>
static void threadRun(void* L) {
int top = lua_gettop(L);
int status = lua_pcall(L, top - 2, 0, 1);
lua_pushinteger(L, status);
}
static char* threadRunner(Thread* thread, Blob* body, Variant* arguments, uint32_t argumentCount) {
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luax_preload(L);
lovrSetErrorCallback((errorFn*) luax_vthrow, L);
lua_pushcfunction(L, luax_getstack);
int errhandler = lua_gettop(L);
if (!luaL_loadbuffer(L, body->data, body->size, "thread")) {
for (uint32_t i = 0; i < argumentCount; i++) {
luax_pushvariant(L, &arguments[i]);
}
if (!lua_pcall(L, argumentCount, 0, errhandler)) {
lovrTry(threadRun, L, luax_vthrow, L);
if (lua_tointeger(L, -1) == 0) {
lua_close(L);
return NULL;
} else {
lua_pop(L, 1);
}
}
// Error handling
size_t length;
const char* message = lua_tolstring(L, -1, &length);
char* error = message ? malloc(length + 1) : NULL;
if (error) {
if (message) {
char* error = lovrMalloc(length + 1);
memcpy(error, message, length + 1);
lua_close(L);
return error;
@ -42,13 +51,13 @@ static char* threadRunner(Thread* thread, Blob* body, Variant* arguments, uint32
}
static int l_lovrThreadNewThread(lua_State* L) {
uint32_t defer = lovrDeferPush();
Blob* blob = luax_totype(L, 1, Blob);
if (!blob) {
size_t length;
const char* str = luaL_checklstring(L, 1, &length);
if (memchr(str, '\n', MIN(1024, length))) {
void* data = malloc(length + 1);
lovrAssert(data, "Out of memory");
void* data = lovrMalloc(length + 1);
memcpy(data, str, length + 1);
blob = lovrBlobCreate(data, length, "thread code");
} else {
@ -56,13 +65,12 @@ static int l_lovrThreadNewThread(lua_State* L) {
lovrAssert(code, "Could not read thread code from file '%s'", str);
blob = lovrBlobCreate(code, length, str);
}
} else {
lovrRetain(blob);
lovrDeferRelease(blob, lovrBlobDestroy);
}
Thread* thread = lovrThreadCreate(threadRunner, blob);
luax_pushtype(L, Thread, thread);
lovrRelease(thread, lovrThreadDestroy);
lovrRelease(blob, lovrBlobDestroy);
lovrDeferPop(defer);
return 1;
}
@ -88,7 +96,24 @@ int luaopen_lovr_thread(lua_State* L) {
luax_register(L, lovrThreadModule);
luax_registertype(L, Thread);
luax_registertype(L, Channel);
lovrThreadModuleInit();
int32_t workers = -1;
luax_pushconf(L);
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "thread");
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "workers");
if (lua_type(L, -1) == LUA_TNUMBER) {
workers = lua_tointeger(L, -1);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
lovrThreadModuleInit(workers);
luax_atexit(L, lovrThreadModuleDestroy);
return 1;
}

View File

@ -82,6 +82,7 @@ typedef enum {
GPU_FORMAT_RGB10A2,
GPU_FORMAT_RG11B10F,
GPU_FORMAT_D16,
GPU_FORMAT_D24,
GPU_FORMAT_D32F,
GPU_FORMAT_D24S8,
GPU_FORMAT_D32FS8,
@ -122,6 +123,7 @@ typedef struct {
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
const char* label;
} gpu_texture_view_info;
typedef struct {
@ -245,16 +247,16 @@ void gpu_layout_destroy(gpu_layout* layout);
// Shader
typedef struct {
uint32_t stage;
const void* code;
size_t length;
} gpu_shader_stage;
} gpu_shader_source;
typedef struct {
gpu_shader_stage vertex;
gpu_shader_stage fragment;
gpu_shader_stage compute;
gpu_layout* layouts[4];
uint32_t stageCount;
gpu_shader_source* stages;
uint32_t pushConstantSize;
gpu_layout* layouts[4];
const char* label;
} gpu_shader_info;
@ -371,6 +373,7 @@ typedef enum {
GPU_TYPE_U8x4,
GPU_TYPE_SN8x4,
GPU_TYPE_UN8x4,
GPU_TYPE_SN10x3,
GPU_TYPE_UN10x3,
GPU_TYPE_I16,
GPU_TYPE_I16x2,
@ -577,8 +580,9 @@ typedef enum {
GPU_PHASE_DEPTH_EARLY = (1 << 6),
GPU_PHASE_DEPTH_LATE = (1 << 7),
GPU_PHASE_COLOR = (1 << 8),
GPU_PHASE_TRANSFER = (1 << 9),
GPU_PHASE_ALL = (1 << 10)
GPU_PHASE_COPY = (1 << 9),
GPU_PHASE_CLEAR = (1 << 10),
GPU_PHASE_BLIT = (1 << 11)
} gpu_phase;
typedef enum {

View File

@ -106,11 +106,13 @@ typedef enum {
GPU_MEMORY_BUFFER_DOWNLOAD,
GPU_MEMORY_TEXTURE_COLOR,
GPU_MEMORY_TEXTURE_D16,
GPU_MEMORY_TEXTURE_D24,
GPU_MEMORY_TEXTURE_D32F,
GPU_MEMORY_TEXTURE_D24S8,
GPU_MEMORY_TEXTURE_D32FS8,
GPU_MEMORY_TEXTURE_LAZY_COLOR,
GPU_MEMORY_TEXTURE_LAZY_D16,
GPU_MEMORY_TEXTURE_LAZY_D24,
GPU_MEMORY_TEXTURE_LAZY_D32F,
GPU_MEMORY_TEXTURE_LAZY_D24S8,
GPU_MEMORY_TEXTURE_LAZY_D32FS8,
@ -184,7 +186,7 @@ static struct {
VkDebugUtilsMessengerEXT messenger;
gpu_allocator allocators[GPU_MEMORY_COUNT];
uint8_t allocatorLookup[GPU_MEMORY_COUNT];
gpu_memory memory[256];
gpu_memory memory[1024];
uint32_t streamCount;
uint32_t tick[2];
gpu_tick ticks[2];
@ -213,11 +215,11 @@ 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 VkBufferUsageFlags getBufferUsage(gpu_buffer_type type);
static bool transitionAttachment(gpu_texture* texture, bool begin, bool resolve, bool discard, VkImageMemoryBarrier* barrier);
static bool transitionAttachment(gpu_texture* texture, bool begin, bool resolve, bool discard, VkImageMemoryBarrier2KHR* barrier);
static VkImageLayout getNaturalLayout(uint32_t usage, VkImageAspectFlags aspect);
static VkFormat convertFormat(gpu_texture_format format, int colorspace);
static VkPipelineStageFlags convertPhase(gpu_phase phase, bool dst);
static VkAccessFlags convertCache(gpu_cache cache);
static VkPipelineStageFlags2 convertPhase(gpu_phase phase, bool dst);
static VkAccessFlags2 convertCache(gpu_cache cache);
static VkBool32 relay(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, const VkDebugUtilsMessengerCallbackDataEXT* data, void* userdata);
static void nickname(void* object, VkObjectType type, const char* name);
static bool vcheck(VkResult result, const char* message);
@ -275,7 +277,7 @@ static bool check(bool condition, const char* message);
X(vkWaitForFences)\
X(vkCreateSemaphore)\
X(vkDestroySemaphore)\
X(vkCmdPipelineBarrier)\
X(vkCmdPipelineBarrier2KHR)\
X(vkCreateQueryPool)\
X(vkDestroyQueryPool)\
X(vkCmdResetQueryPool)\
@ -412,6 +414,7 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
switch (info->format) {
case GPU_FORMAT_D16: texture->aspect = VK_IMAGE_ASPECT_DEPTH_BIT; break;
case GPU_FORMAT_D24: texture->aspect = VK_IMAGE_ASPECT_DEPTH_BIT; break;
case GPU_FORMAT_D32F: texture->aspect = VK_IMAGE_ASPECT_DEPTH_BIT; break;
case GPU_FORMAT_D24S8: texture->aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; break;
case GPU_FORMAT_D32FS8: texture->aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; break;
@ -436,20 +439,22 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
return gpu_texture_init_view(texture, &viewInfo);
}
bool mutableFormat = info->srgb && (info->usage & GPU_TEXTURE_STORAGE);
VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags =
(info->type == GPU_TEXTURE_3D ? VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT : 0) |
(info->type == GPU_TEXTURE_CUBE ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0) |
(info->srgb ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0),
(mutableFormat ? (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT) : 0),
.imageType = imageTypes[info->type],
.format = convertFormat(texture->format, LINEAR),
.format = convertFormat(texture->format, info->srgb),
.extent.width = info->size[0],
.extent.height = info->size[1],
.extent.depth = texture->layers ? 1 : info->size[2],
.mipLevels = info->mipmaps,
.arrayLayers = texture->layers ? texture->layers : 1,
.samples = info->samples,
.samples = info->samples ? info->samples : 1,
.usage =
(((info->usage & GPU_TEXTURE_RENDER) && texture->aspect == VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) |
(((info->usage & GPU_TEXTURE_RENDER) && texture->aspect != VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0) |
@ -464,7 +469,7 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
VkFormat formats[2];
VkImageFormatListCreateInfo imageFormatList;
if (info->srgb && state.extensions.formatList) {
if (mutableFormat && state.extensions.formatList) {
imageFormatList = (VkImageFormatListCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO,
.viewFormatCount = COUNTOF(formats),
@ -472,12 +477,9 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
};
formats[0] = imageInfo.format;
formats[1] = convertFormat(texture->format, SRGB);
if (formats[0] != formats[1]) {
imageFormatList.pNext = imageInfo.pNext;
imageInfo.pNext = &imageFormatList;
}
formats[1] = convertFormat(texture->format, LINEAR);
imageFormatList.pNext = imageInfo.pNext;
imageInfo.pNext = &imageFormatList;
}
VK(vkCreateImage(state.device, &imageInfo, NULL, &texture->handle), "Could not create texture") return false;
@ -488,6 +490,7 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
switch (info->format) {
case GPU_FORMAT_D16: memoryType = transient ? GPU_MEMORY_TEXTURE_LAZY_D16 : GPU_MEMORY_TEXTURE_D16; break;
case GPU_FORMAT_D24: memoryType = transient ? GPU_MEMORY_TEXTURE_LAZY_D24 : GPU_MEMORY_TEXTURE_D24; break;
case GPU_FORMAT_D32F: memoryType = transient ? GPU_MEMORY_TEXTURE_LAZY_D32F : GPU_MEMORY_TEXTURE_D32F; break;
case GPU_FORMAT_D24S8: memoryType = transient ? GPU_MEMORY_TEXTURE_LAZY_D24S8 : GPU_MEMORY_TEXTURE_D24S8; break;
case GPU_FORMAT_D32FS8: memoryType = transient ? GPU_MEMORY_TEXTURE_LAZY_D32FS8 : GPU_MEMORY_TEXTURE_D32FS8; break;
@ -506,7 +509,6 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
}
bool needsView = info->usage & (GPU_TEXTURE_RENDER | GPU_TEXTURE_SAMPLE | GPU_TEXTURE_STORAGE);
if (info->usage == GPU_TEXTURE_STORAGE && info->srgb) needsView = false;
if (needsView && !gpu_texture_init_view(texture, &viewInfo)) {
vkDestroyImage(state.device, texture->handle, NULL);
@ -520,11 +522,11 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
uint32_t levelCount = info->upload.levelCount;
gpu_buffer* buffer = info->upload.buffer;
VkPipelineStageFlags prev, next;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageMemoryBarrier transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
VkImageMemoryBarrier2KHR transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR,
.image = image,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
@ -532,10 +534,24 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
VkDependencyInfoKHR barrier = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.pImageMemoryBarriers = &transition,
.imageMemoryBarrierCount = 1
};
if (levelCount > 0) {
VkBufferImageCopy regions[16];
transition.srcStageMask = VK_PIPELINE_STAGE_2_NONE_KHR;
transition.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT_KHR;
transition.srcAccessMask = VK_ACCESS_2_NONE_KHR;
transition.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR;
transition.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
transition.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdPipelineBarrier2KHR(commands, &barrier);
VkBufferImageCopy copies[16];
for (uint32_t i = 0; i < levelCount; i++) {
regions[i] = (VkBufferImageCopy) {
copies[i] = (VkBufferImageCopy) {
.bufferOffset = info->upload.levelOffsets[i],
.imageSubresource.aspectMask = texture->aspect,
.imageSubresource.mipLevel = i,
@ -547,27 +563,20 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
};
}
// Upload initial contents
prev = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
next = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.srcAccessMask = 0;
transition.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.oldLayout = layout;
transition.newLayout = layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
vkCmdCopyBufferToImage(commands, buffer->handle, image, layout, levelCount, regions);
vkCmdCopyBufferToImage(commands, buffer->handle, image, transition.newLayout, levelCount, copies);
// Generate mipmaps
if (info->upload.generateMipmaps) {
prev = VK_PIPELINE_STAGE_TRANSFER_BIT;
next = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
transition.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT_KHR;
transition.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT_KHR;
transition.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR;
transition.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT_KHR;
transition.oldLayout = transition.newLayout;
transition.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
transition.subresourceRange.baseMipLevel = 0;
transition.subresourceRange.levelCount = levelCount;
transition.oldLayout = layout;
transition.newLayout = layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
vkCmdPipelineBarrier2KHR(commands, &barrier);
for (uint32_t i = levelCount; i < info->mipmaps; i++) {
VkImageBlit region = {
.srcSubresource = {
@ -584,25 +593,30 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
.dstOffsets[1] = { MAX(info->size[0] >> i, 1), MAX(info->size[1] >> i, 1), 1 }
};
vkCmdBlitImage(commands, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, VK_FILTER_LINEAR);
transition.subresourceRange.baseMipLevel = i;
transition.subresourceRange.levelCount = 1;
transition.srcStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT_KHR;
transition.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT_KHR;
transition.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR;
transition.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT_KHR;
transition.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
transition.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
transition.subresourceRange.baseMipLevel = i;
transition.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier2KHR(commands, &barrier);
}
}
}
// Transition to natural layout
prev = levelCount > 0 ? VK_PIPELINE_STAGE_TRANSFER_BIT : VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
next = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
transition.srcAccessMask = levelCount > 0 ? VK_ACCESS_TRANSFER_WRITE_BIT : 0;
transition.dstAccessMask = 0;
transition.oldLayout = layout;
transition.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT_KHR | VK_PIPELINE_STAGE_2_BLIT_BIT_KHR;
transition.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR;
transition.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR;
transition.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT_KHR | VK_ACCESS_2_MEMORY_WRITE_BIT_KHR;
transition.oldLayout = transition.newLayout;
transition.newLayout = texture->layout;
transition.subresourceRange.baseMipLevel = 0;
transition.subresourceRange.levelCount = info->mipmaps;
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
vkCmdPipelineBarrier2KHR(commands, &barrier);
}
texture->memory = memory - state.memory;
@ -638,6 +652,11 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
((info->usage & GPU_TEXTURE_STORAGE) && !texture->srgb ? VK_IMAGE_USAGE_STORAGE_BIT : 0)
};
if (usage.usage == 0) {
texture->view = VK_NULL_HANDLE;
return true;
}
VkImageViewCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &usage,
@ -657,6 +676,8 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
return false;
}
nickname(texture->view, VK_OBJECT_TYPE_IMAGE_VIEW, info->label);
return true;
}
@ -976,31 +997,28 @@ void gpu_layout_destroy(gpu_layout* layout) {
// Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) {
struct { VkShaderStageFlags flags; gpu_shader_stage* source; } stages[] = {
{ VK_SHADER_STAGE_VERTEX_BIT, &info->vertex },
{ VK_SHADER_STAGE_FRAGMENT_BIT, &info->fragment },
{ VK_SHADER_STAGE_COMPUTE_BIT, &info->compute }
};
uint32_t stageCount = 0;
VkShaderStageFlags stageFlags = 0;
for (uint32_t i = 0; i < COUNTOF(stages); i++) {
if (!stages[i].source->code) continue;
for (uint32_t i = 0; i < info->stageCount; i++) {
switch (info->stages[i].stage) {
case GPU_STAGE_VERTEX: stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; break;
case GPU_STAGE_FRAGMENT: stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; break;
case GPU_STAGE_COMPUTE: stageFlags |= VK_SHADER_STAGE_COMPUTE_BIT; break;
default: return false;
}
}
for (uint32_t i = 0; i < info->stageCount; i++) {
VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = stages[i].source->length,
.pCode = stages[i].source->code
.codeSize = info->stages[i].length,
.pCode = info->stages[i].code
};
VK(vkCreateShaderModule(state.device, &moduleInfo, NULL, &shader->handles[stageCount]), "Failed to load shader") {
VK(vkCreateShaderModule(state.device, &moduleInfo, NULL, &shader->handles[i]), "Failed to load shader") {
return false;
}
nickname(shader->handles[i], VK_OBJECT_TYPE_SHADER_MODULE, info->label);
stageFlags |= stages[i].flags;
stageCount++;
}
VkDescriptorSetLayout layouts[4];
@ -1215,7 +1233,7 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
for (uint32_t i = 0; i < info->colorCount; i++) {
references[i] = (VkAttachmentReference2) {
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.layout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.attachment = i
};
@ -1236,7 +1254,7 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
references[index] = (VkAttachmentReference2) {
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.layout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.attachment = index
};
@ -1259,7 +1277,7 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
references[index] = (VkAttachmentReference2) {
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.layout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.attachment = index
};
@ -1278,7 +1296,7 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
if (info->resolveDepth) {
references[index + 1] = (VkAttachmentReference2) {
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.layout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.attachment = index + 1
};
@ -1357,6 +1375,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
[GPU_TYPE_U8x4] = VK_FORMAT_R8G8B8A8_UINT,
[GPU_TYPE_SN8x4] = VK_FORMAT_R8G8B8A8_SNORM,
[GPU_TYPE_UN8x4] = VK_FORMAT_R8G8B8A8_UNORM,
[GPU_TYPE_SN10x3] = VK_FORMAT_A2B10G10R10_SNORM_PACK32,
[GPU_TYPE_UN10x3] = VK_FORMAT_A2B10G10R10_UNORM_PACK32,
[GPU_TYPE_I16] = VK_FORMAT_R16_SINT,
[GPU_TYPE_I16x2] = VK_FORMAT_R16G16_SINT,
@ -1578,7 +1597,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
.pData = (const void*) constants
};
uint32_t stageCount = info->shader->handles[1] && info->pass->colorCount > 0 ? 2 : 1;
uint32_t stageCount = info->shader->handles[1] ? 2 : 1;
VkPipelineShaderStageCreateInfo shaders[2] = {
{
@ -1781,7 +1800,7 @@ void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {
// Layout transitions
uint32_t barrierCount = 0;
VkImageMemoryBarrier barriers[10];
VkImageMemoryBarrier2KHR barriers[10];
bool BEGIN = true;
bool RESOLVE = true;
@ -1799,13 +1818,11 @@ void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {
}
if (barrierCount > 0) {
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkPipelineStageFlags dstStage =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
vkCmdPipelineBarrier(stream->commands, srcStage, dstStage, 0, 0, NULL, 0, NULL, barrierCount, barriers);
vkCmdPipelineBarrier2KHR(stream->commands, &(VkDependencyInfoKHR) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.imageMemoryBarrierCount = barrierCount,
.pImageMemoryBarriers = barriers
});
}
// Do it!
@ -1834,7 +1851,7 @@ void gpu_render_end(gpu_stream* stream, gpu_canvas* canvas) {
// Layout transitions
uint32_t barrierCount = 0;
VkImageMemoryBarrier barriers[10];
VkImageMemoryBarrier2KHR barriers[10];
bool BEGIN = true;
bool RESOLVE = true;
@ -1849,13 +1866,11 @@ void gpu_render_end(gpu_stream* stream, gpu_canvas* canvas) {
barrierCount += transitionAttachment(canvas->depth.resolve, !BEGIN, RESOLVE, !DISCARD, &barriers[barrierCount]);
if (barrierCount > 0) {
VkPipelineStageFlags srcStage =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
VkPipelineStageFlags dstStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
vkCmdPipelineBarrier(stream->commands, srcStage, dstStage, 0, 0, NULL, 0, NULL, barrierCount, barriers);
vkCmdPipelineBarrier2KHR(stream->commands, &(VkDependencyInfoKHR) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.imageMemoryBarrierCount = barrierCount,
.pImageMemoryBarriers = barriers
});
}
}
@ -2055,20 +2070,22 @@ void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t s
}
void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count) {
VkMemoryBarrier memoryBarrier = { .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER };
VkPipelineStageFlags src = 0;
VkPipelineStageFlags dst = 0;
VkMemoryBarrier2KHR memoryBarrier = { .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR };
for (uint32_t i = 0; i < count; i++) {
gpu_barrier* barrier = &barriers[i];
src |= convertPhase(barrier->prev, false);
dst |= convertPhase(barrier->next, true);
memoryBarrier.srcStageMask |= convertPhase(barrier->prev, false);
memoryBarrier.dstStageMask |= convertPhase(barrier->next, true);
memoryBarrier.srcAccessMask |= convertCache(barrier->flush);
memoryBarrier.dstAccessMask |= convertCache(barrier->clear);
}
if (src && dst) {
vkCmdPipelineBarrier(stream->commands, src, dst, 0, 1, &memoryBarrier, 0, NULL, 0, NULL);
if (memoryBarrier.srcStageMask && memoryBarrier.dstStageMask) {
vkCmdPipelineBarrier2KHR(stream->commands, &(VkDependencyInfoKHR) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.pMemoryBarriers = &memoryBarrier,
.memoryBarrierCount = 1
});
}
}
@ -2081,67 +2098,49 @@ void gpu_tally_finish(gpu_stream* stream, gpu_tally* tally, uint32_t index) {
}
void gpu_tally_mark(gpu_stream* stream, gpu_tally* tally, uint32_t index) {
vkCmdWriteTimestamp(stream->commands, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, tally->handle, index);
vkCmdWriteTimestamp(stream->commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, tally->handle, index);
}
// Acquires an OpenXR swapchain texture, transitioning it to the natural layout
void gpu_xr_acquire(gpu_stream* stream, gpu_texture* texture) {
VkImageLayout attachmentLayout = texture->aspect == VK_IMAGE_ASPECT_COLOR_BIT ?
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// If the texture only has the RENDER usage, its natural layout matches the layout that OpenXR
// gives us the texture in, so no layout transition is needed.
if (texture->layout == attachmentLayout) {
return;
}
VkImageMemoryBarrier transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
.oldLayout = attachmentLayout,
.newLayout = texture->layout,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
VkPipelineStageFlags prev = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags next = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkCmdPipelineBarrier(stream->commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
vkCmdPipelineBarrier2KHR(stream->commands, &(VkDependencyInfoKHR) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &(VkImageMemoryBarrier2KHR) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR,
.srcStageMask = VK_PIPELINE_STAGE_2_NONE_KHR,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR,
.srcAccessMask = VK_ACCESS_2_NONE_KHR,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT_KHR | VK_ACCESS_2_MEMORY_WRITE_BIT_KHR,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.newLayout = texture->layout,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
}
});
}
// Releases an OpenXR swapchain texture, transitioning it back to the layout expected by OpenXR
void gpu_xr_release(gpu_stream* stream, gpu_texture* texture) {
VkImageLayout attachmentLayout = texture->aspect == VK_IMAGE_ASPECT_COLOR_BIT ?
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// If the texture only has the RENDER usage, its natural layout matches the layout that OpenXR
// expects the texture to be in, so no layout transition is needed.
if (texture->layout == attachmentLayout) {
return;
}
VkImageMemoryBarrier transition = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = texture->layout,
.newLayout = attachmentLayout,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
VkPipelineStageFlags prev = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkPipelineStageFlags next = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
vkCmdPipelineBarrier(stream->commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
vkCmdPipelineBarrier2KHR(stream->commands, &(VkDependencyInfoKHR) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &(VkImageMemoryBarrier2KHR) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR,
.dstStageMask = VK_PIPELINE_STAGE_2_NONE_KHR,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT_KHR,
.dstAccessMask = VK_ACCESS_2_NONE_KHR,
.oldLayout = texture->layout,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
}
});
}
// Entry
@ -2208,7 +2207,6 @@ bool gpu_init(gpu_config* config) {
{ "VK_KHR_portability_enumeration", true, &state.extensions.portability },
{ "VK_EXT_debug_utils", config->debug, &state.extensions.debug },
{ "VK_EXT_swapchain_colorspace", true, &state.extensions.colorspace },
{ "VK_KHR_image_format_list", true, &state.extensions.formatList },
{ "VK_KHR_surface", true, &state.extensions.surface },
#if defined(_WIN32)
{ "VK_KHR_win32_surface", true, &state.extensions.surfaceOS },
@ -2344,8 +2342,14 @@ bool gpu_init(gpu_config* config) {
config->limits->pointSize = limits->pointSizeRange[1];
}
VkPhysicalDeviceSynchronization2FeaturesKHR synchronization2Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR,
.synchronization2 = true
};
VkPhysicalDeviceShaderDrawParameterFeatures shaderDrawParameterFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES,
.pNext = &synchronization2Features
};
VkPhysicalDeviceMultiviewFeatures multiviewFeatures = {
@ -2368,6 +2372,7 @@ bool gpu_init(gpu_config* config) {
enable->fullDrawIndexUint32 = true;
enable->imageCubeArray = true;
enable->independentBlend = true;
synchronization2Features.synchronization2 = true;
multiviewFeatures.multiview = true;
shaderDrawParameterFeatures.shaderDrawParameters = true;
@ -2435,7 +2440,9 @@ bool gpu_init(gpu_config* config) {
{ "VK_KHR_swapchain", true, &state.extensions.swapchain },
{ "VK_KHR_portability_subset", true, &state.extensions.portability },
{ "VK_KHR_depth_stencil_resolve", true, &state.extensions.depthResolve },
{ "VK_KHR_shader_non_semantic_info", config->debug, &state.extensions.shaderDebug }
{ "VK_KHR_shader_non_semantic_info", config->debug, &state.extensions.shaderDebug },
{ "VK_KHR_image_format_list", true, &state.extensions.formatList },
{ "VK_KHR_synchronization2", true, NULL }
};
uint32_t extensionCount = 0;
@ -2559,11 +2566,13 @@ bool gpu_init(gpu_config* config) {
struct { VkFormat format; VkImageUsageFlags usage; } imageFlags[] = {
[GPU_MEMORY_TEXTURE_COLOR] = { VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_D16] = { VK_FORMAT_D16_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_D24] = { VK_FORMAT_X8_D24_UNORM_PACK32, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_D32F] = { VK_FORMAT_D32_SFLOAT, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_D24S8] = { VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_D32FS8] = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_USAGE_SAMPLED_BIT },
[GPU_MEMORY_TEXTURE_LAZY_COLOR] = { VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transient },
[GPU_MEMORY_TEXTURE_LAZY_D16] = { VK_FORMAT_D16_UNORM, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transient },
[GPU_MEMORY_TEXTURE_LAZY_D24] = { VK_FORMAT_X8_D24_UNORM_PACK32, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transient },
[GPU_MEMORY_TEXTURE_LAZY_D32F] = { VK_FORMAT_D32_SFLOAT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transient },
[GPU_MEMORY_TEXTURE_LAZY_D24S8] = { VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transient },
[GPU_MEMORY_TEXTURE_LAZY_D32FS8] = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transient }
@ -2734,7 +2743,7 @@ void gpu_submit(gpu_stream** streams, uint32_t count) {
commands[i] = streams[i]->commands;
}
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR;
VkSubmitInfo submit = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
@ -2766,6 +2775,7 @@ bool gpu_wait_tick(uint32_t tick) {
void gpu_wait_idle(void) {
vkDeviceWaitIdle(state.device);
state.tick[GPU] = state.tick[CPU];
}
uintptr_t gpu_vk_get_instance(void) {
@ -2796,11 +2806,13 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
[GPU_MEMORY_BUFFER_DOWNLOAD] = 0,
[GPU_MEMORY_TEXTURE_COLOR] = 1 << 28,
[GPU_MEMORY_TEXTURE_D16] = 1 << 28,
[GPU_MEMORY_TEXTURE_D24] = 1 << 28,
[GPU_MEMORY_TEXTURE_D32F] = 1 << 28,
[GPU_MEMORY_TEXTURE_D24S8] = 1 << 28,
[GPU_MEMORY_TEXTURE_D32FS8] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_COLOR] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_D16] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_D24] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_D32F] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_D24S8] = 1 << 28,
[GPU_MEMORY_TEXTURE_LAZY_D32FS8] = 1 << 28
@ -2954,43 +2966,43 @@ static VkBufferUsageFlags getBufferUsage(gpu_buffer_type type) {
}
}
static bool transitionAttachment(gpu_texture* texture, bool begin, bool resolve, bool discard, VkImageMemoryBarrier* barrier) {
if (!texture) {
static bool transitionAttachment(gpu_texture* texture, bool begin, bool resolve, bool discard, VkImageMemoryBarrier2KHR* barrier) {
if (!texture || texture->layout == VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR) {
return false;
}
bool depth = texture->aspect != VK_IMAGE_ASPECT_COLOR_BIT;
VkImageLayout renderLayout = depth ?
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkPipelineStageFlags2 stage = depth ?
(VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR ) :
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR;
VkAccessFlags access = (depth && !resolve) ?
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) :
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
if (texture->layout == renderLayout) {
return false;
}
VkAccessFlags2 access = (depth && !resolve) ?
(VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR) :
(VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR);
if (begin) {
*barrier = (VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
*barrier = (VkImageMemoryBarrier2KHR) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR,
.srcAccessMask = VK_ACCESS_2_NONE_KHR,
.dstStageMask = stage,
.dstAccessMask = access,
.oldLayout = discard || resolve ? VK_IMAGE_LAYOUT_UNDEFINED : texture->layout,
.newLayout = renderLayout,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
};
} else {
*barrier = (VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
*barrier = (VkImageMemoryBarrier2KHR) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR,
.srcStageMask = stage,
.srcAccessMask = access,
.dstAccessMask = 0,
.oldLayout = renderLayout,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR,
.dstAccessMask = VK_ACCESS_2_NONE_KHR,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR,
.newLayout = texture->layout,
.image = texture->handle,
.subresourceRange.aspectMask = texture->aspect,
@ -3006,13 +3018,9 @@ static VkImageLayout getNaturalLayout(uint32_t usage, VkImageAspectFlags aspect)
if (usage & (GPU_TEXTURE_STORAGE | GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST)) {
return VK_IMAGE_LAYOUT_GENERAL;
} else if (usage & GPU_TEXTURE_SAMPLE) {
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR;
} else {
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
} else {
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
return VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
}
return VK_IMAGE_LAYOUT_UNDEFINED;
}
@ -3036,6 +3044,7 @@ static VkFormat convertFormat(gpu_texture_format format, int colorspace) {
[GPU_FORMAT_RGB10A2] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UNORM_PACK32 },
[GPU_FORMAT_RG11B10F] = { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_B10G11R11_UFLOAT_PACK32 },
[GPU_FORMAT_D16] = { VK_FORMAT_D16_UNORM, VK_FORMAT_D16_UNORM },
[GPU_FORMAT_D24] = { VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_X8_D24_UNORM_PACK32 },
[GPU_FORMAT_D32F] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT },
[GPU_FORMAT_D24S8] = { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
[GPU_FORMAT_D32FS8] = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT },
@ -3072,37 +3081,38 @@ static VkFormat convertFormat(gpu_texture_format format, int colorspace) {
return formats[format][colorspace];
}
static VkPipelineStageFlags convertPhase(gpu_phase phase, bool dst) {
VkPipelineStageFlags flags = 0;
if (phase & GPU_PHASE_INDIRECT) flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
if (phase & GPU_PHASE_INPUT_INDEX) flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
if (phase & GPU_PHASE_INPUT_VERTEX) flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
if (phase & GPU_PHASE_SHADER_VERTEX) flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
if (phase & GPU_PHASE_SHADER_FRAGMENT) flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
if (phase & GPU_PHASE_SHADER_COMPUTE) flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (phase & GPU_PHASE_DEPTH_EARLY) flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
if (phase & GPU_PHASE_DEPTH_LATE) flags |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
if (phase & GPU_PHASE_COLOR) flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
if (phase & GPU_PHASE_TRANSFER) flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
if (phase & GPU_PHASE_ALL) flags |= dst ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT : VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
static VkPipelineStageFlags2 convertPhase(gpu_phase phase, bool dst) {
VkPipelineStageFlags2 flags = 0;
if (phase & GPU_PHASE_INDIRECT) flags |= VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR;
if (phase & GPU_PHASE_INPUT_INDEX) flags |= VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR;
if (phase & GPU_PHASE_INPUT_VERTEX) flags |= VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR;
if (phase & GPU_PHASE_SHADER_VERTEX) flags |= VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR;
if (phase & GPU_PHASE_SHADER_FRAGMENT) flags |= VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR;
if (phase & GPU_PHASE_SHADER_COMPUTE) flags |= VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR;
if (phase & GPU_PHASE_DEPTH_EARLY) flags |= VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR;
if (phase & GPU_PHASE_DEPTH_LATE) flags |= VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR;
if (phase & GPU_PHASE_COLOR) flags |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR;
if (phase & GPU_PHASE_COPY) flags |= VK_PIPELINE_STAGE_2_COPY_BIT_KHR;
if (phase & GPU_PHASE_CLEAR) flags |= VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR;
if (phase & GPU_PHASE_BLIT) flags |= VK_PIPELINE_STAGE_2_BLIT_BIT_KHR;
return flags;
}
static VkAccessFlags convertCache(gpu_cache cache) {
VkAccessFlags flags = 0;
if (cache & GPU_CACHE_INDIRECT) flags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
if (cache & GPU_CACHE_INDEX) flags |= VK_ACCESS_INDEX_READ_BIT;
if (cache & GPU_CACHE_VERTEX) flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
if (cache & GPU_CACHE_UNIFORM) flags |= VK_ACCESS_UNIFORM_READ_BIT;
if (cache & GPU_CACHE_TEXTURE) flags |= VK_ACCESS_SHADER_READ_BIT;
if (cache & GPU_CACHE_STORAGE_READ) flags |= VK_ACCESS_SHADER_READ_BIT;
if (cache & GPU_CACHE_STORAGE_WRITE) flags |= VK_ACCESS_SHADER_WRITE_BIT;
if (cache & GPU_CACHE_DEPTH_READ) flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
if (cache & GPU_CACHE_DEPTH_WRITE) flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
if (cache & GPU_CACHE_COLOR_READ) flags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
if (cache & GPU_CACHE_COLOR_WRITE) flags |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if (cache & GPU_CACHE_TRANSFER_READ) flags |= VK_ACCESS_TRANSFER_READ_BIT;
if (cache & GPU_CACHE_TRANSFER_WRITE) flags |= VK_ACCESS_TRANSFER_WRITE_BIT;
static VkAccessFlags2 convertCache(gpu_cache cache) {
VkAccessFlags2 flags = 0;
if (cache & GPU_CACHE_INDIRECT) flags |= VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR;
if (cache & GPU_CACHE_INDEX) flags |= VK_ACCESS_2_INDEX_READ_BIT_KHR;
if (cache & GPU_CACHE_VERTEX) flags |= VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR;
if (cache & GPU_CACHE_UNIFORM) flags |= VK_ACCESS_2_UNIFORM_READ_BIT_KHR;
if (cache & GPU_CACHE_TEXTURE) flags |= VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR;
if (cache & GPU_CACHE_STORAGE_READ) flags |= VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR;
if (cache & GPU_CACHE_STORAGE_WRITE) flags |= VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR;
if (cache & GPU_CACHE_DEPTH_READ) flags |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR;
if (cache & GPU_CACHE_DEPTH_WRITE) flags |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR;
if (cache & GPU_CACHE_COLOR_READ) flags |= VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR;
if (cache & GPU_CACHE_COLOR_WRITE) flags |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR;
if (cache & GPU_CACHE_TRANSFER_READ) flags |= VK_ACCESS_2_TRANSFER_READ_BIT_KHR;
if (cache & GPU_CACHE_TRANSFER_WRITE) flags |= VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR;
return flags;
}

View File

@ -305,6 +305,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
[GPU_TYPE_U8x4] = WGPUVertexFormat_Uint8x4,
[GPU_TYPE_SN8x4] = WGPUVertexFormat_Snorm8x4,
[GPU_TYPE_UN8x4] = WGPUVertexFormat_Unorm8x4,
[GPU_TYPE_SN10x3] = WGPUVertexFormat_Undefined,
[GPU_TYPE_UN10x3] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16x2] = WGPUVertexFormat_Sint16x2,
@ -692,6 +693,7 @@ static WGPUTextureFormat convertFormat(gpu_texture_format format, bool srgb) {
[GPU_FORMAT_RGB10A2] = {WGPUTextureFormat_RGB10A2Unorm, WGPUTextureFormat_RGB10A2Unorm },
[GPU_FORMAT_RG11B10F] = { WGPUTextureFormat_RG11B10Ufloat, WGPUTextureFormat_RG11B10Ufloat },
[GPU_FORMAT_D16] = { WGPUTextureFormat_Depth16Unorm, WGPUTextureFormat_Depth16Unorm },
[GPU_FORMAT_D24] = { WGPUTextureFormat_Depth24Plus, WGPUTextureFormat_Depth24Plus },
[GPU_FORMAT_D32F] = { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Depth32Float },
[GPU_FORMAT_D24S8] = { WGPUTextureFormat_Depth24PlusStencil8, WGPUTextureFormat_Depth24PlusStencil8 },
[GPU_FORMAT_D32FS8] = { WGPUTextureFormat_Depth32FloatStencil8, WGPUTextureFormat_Depth32FloatStencil8 },

136
src/core/job.c Normal file
View File

@ -0,0 +1,136 @@
#include "job.h"
#include <stdatomic.h>
#include <threads.h>
#include <string.h>
#define MAX_WORKERS 64
#define MAX_JOBS 1024
struct job {
job* next;
fn_job* fn;
void* arg;
atomic_uint done;
};
static struct {
job jobs[MAX_JOBS];
thrd_t workers[MAX_WORKERS];
uint32_t workerCount;
job* head;
job* tail;
job* pool;
cnd_t hasJob;
mtx_t lock;
bool quit;
} state;
// Must hold lock, this will unlock it, state.head must exist
static void runJob(void) {
job* job = state.head;
state.head = job->next;
if (!job->next) state.tail = NULL;
mtx_unlock(&state.lock);
job->fn(job->arg);
job->done = true;
}
static int workerLoop(void* arg) {
for (;;) {
mtx_lock(&state.lock);
while (!state.head && !state.quit) {
cnd_wait(&state.hasJob, &state.lock);
}
if (state.quit) {
break;
}
runJob();
}
mtx_unlock(&state.lock);
return 0;
}
bool job_init(uint32_t count) {
mtx_init(&state.lock, mtx_plain);
cnd_init(&state.hasJob);
state.pool = state.jobs;
for (uint32_t i = 0; i < MAX_JOBS - 1; i++) {
state.jobs[i].next = &state.jobs[i + 1];
}
if (count > MAX_WORKERS) count = MAX_WORKERS;
for (uint32_t i = 0; i < count; i++, state.workerCount++) {
if (thrd_create(&state.workers[i], workerLoop, (void*) (uintptr_t) i) != thrd_success) {
return false;
}
}
return true;
}
void job_destroy(void) {
state.quit = true;
cnd_broadcast(&state.hasJob);
for (uint32_t i = 0; i < state.workerCount; i++) {
thrd_join(state.workers[i], NULL);
}
cnd_destroy(&state.hasJob);
mtx_destroy(&state.lock);
memset(&state, 0, sizeof(state));
}
job* job_start(fn_job* fn, void* arg) {
mtx_lock(&state.lock);
if (!state.pool) {
mtx_unlock(&state.lock);
fn(arg);
return NULL;
}
job* job = state.pool;
state.pool = job->next;
if (state.tail) {
state.tail->next = job;
state.tail = job;
} else {
state.head = job;
state.tail = job;
cnd_signal(&state.hasJob);
}
job->next = NULL;
job->done = false;
job->fn = fn;
job->arg = arg;
mtx_unlock(&state.lock);
return job;
}
void job_wait(job* job) {
if (!job) return;
while (!job->done) {
mtx_lock(&state.lock);
if (state.head) {
runJob();
} else {
mtx_unlock(&state.lock);
thrd_yield();
}
}
mtx_lock(&state.lock);
job->next = state.pool;
state.pool = job;
mtx_unlock(&state.lock);
}

13
src/core/job.h Normal file
View File

@ -0,0 +1,13 @@
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#pragma once
typedef struct job job;
typedef void fn_job(void* arg);
bool job_init(uint32_t workerCount);
void job_destroy(void);
job* job_start(fn_job* fn, void* arg);
void job_wait(job* job);

View File

@ -160,6 +160,8 @@ void os_open_console(void);
double os_get_time(void);
void os_sleep(double seconds);
void os_request_permission(os_permission permission);
const char* os_get_clipboard_text(void);
void os_set_clipboard_text(const char* text);
void* os_vm_init(size_t size);
bool os_vm_free(void* p, size_t size);

View File

@ -269,6 +269,14 @@ void os_request_permission(os_permission permission) {
}
}
const char* os_get_clipboard_text(void) {
return NULL;
}
void os_set_clipboard_text(const char* text) {
//
}
void* os_vm_init(size_t size) {
return mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

View File

@ -1,5 +1,13 @@
#ifndef LOVR_USE_GLFW
const char* os_get_clipboard_text(void) {
return NULL;
}
void os_set_clipboard_text(const char* text) {
//
}
void os_poll_events(void) {
//
}
@ -317,12 +325,22 @@ static int convertKey(os_key key) {
case OS_KEY_RIGHT: return GLFW_KEY_RIGHT;
case OS_KEY_LEFT_SHIFT: return GLFW_KEY_LEFT_SHIFT;
case OS_KEY_RIGHT_SHIFT: return GLFW_KEY_RIGHT_SHIFT;
case OS_KEY_LEFT_CONTROL: return GLFW_KEY_LEFT_CONTROL;
case OS_KEY_RIGHT_CONTROL: return GLFW_KEY_RIGHT_CONTROL;
case OS_KEY_ESCAPE: return GLFW_KEY_ESCAPE;
case OS_KEY_F5: return GLFW_KEY_F5;
default: return GLFW_KEY_UNKNOWN;
}
}
const char* os_get_clipboard_text(void) {
return glfwGetClipboardString(NULL);
}
void os_set_clipboard_text(const char* text) {
glfwSetClipboardString(NULL, text);
}
void os_poll_events(void) {
if (glfwState.window) {
glfwPollEvents();

View File

@ -246,6 +246,14 @@ void os_request_permission(os_permission permission) {
//
}
const char* os_get_clipboard_text(void) {
return NULL;
}
void os_set_clipboard_text(const char* text) {
//
}
void os_thread_attach(void) {
//
}

View File

@ -20,8 +20,7 @@ typedef union {
uint16_t name;
} attribute;
struct {
uint8_t set;
uint8_t binding;
uint16_t decoration;
uint16_t name;
} variable;
struct {
@ -220,8 +219,12 @@ static spv_result spv_parse_decoration(spv_context* spv, const uint32_t* op, spv
case 1: spv->cache[id].flag.number = op[3]; break; // SpecID
case 6: spv->cache[id].type.arrayStride = op[3]; break; // ArrayStride (overrides name)
case 30: spv->cache[id].attribute.location = op[3]; break; // Location
case 33: spv->cache[id].variable.binding = op[3]; break; // Binding
case 34: spv->cache[id].variable.set = op[3]; break; // Set
case 33:
case 34:
if (spv->cache[id].variable.decoration == 0xffff) {
spv->cache[id].variable.decoration = op - spv->words;
}
break;
default: break;
}
@ -352,11 +355,8 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
return spv_parse_field(spv, type, info->pushConstants, info);
}
uint32_t set = spv->cache[variableId].variable.set;
uint32_t binding = spv->cache[variableId].variable.binding;
// Ignore output variables (storageClass 3) and anything without a set/binding decoration
if (storageClass == 3 || set == 0xff || binding == 0xff) {
if (storageClass == 3 || spv->cache[variableId].variable.decoration == 0xffff) {
return SPV_OK;
}
@ -374,8 +374,38 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
spv_resource* resource = &info->resources[info->resourceCount++];
resource->set = set;
resource->binding = binding;
// Resolve the set/binding pointers. The cache stores the index of the first set/binding
// decoration word, we need to search for the "other" one.
const uint32_t* word = spv->words + spv->cache[variableId].variable.decoration;
bool set = word[2] == 34;
uint32_t other = set ? 33 : 34;
if (set) {
resource->set = &word[3];
resource->binding = NULL;
} else {
resource->set = NULL;
resource->binding = &word[3];
}
for (;;) {
const uint32_t* next = word + OP_LENGTH(word);
if (word == next || next > spv->edge || (OP_CODE(next) != 71 && OP_CODE(next) != 72)) {
break;
}
word = next;
if (OP_CODE(word) == 71 && word[1] == variableId && word[2] == other) {
if (set) {
resource->binding = &word[3];
} else {
resource->set = &word[3];
}
break;
}
}
// If it's an array, read the array size and unwrap the inner type
if (OP_CODE(type) == 28) { // OpTypeArray
@ -394,9 +424,9 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
return SPV_INVALID;
}
resource->count = length[3];
resource->arraySize = length[3];
} else {
resource->count = 0;
resource->arraySize = 0;
}
// Buffers
@ -413,12 +443,12 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
}
info->fieldCount++;
resource->fields = spv->fields++;
resource->fields[0].offset = 0;
resource->fields[0].name = resource->name;
return spv_parse_field(spv, type, resource->fields, info);
resource->bufferFields = spv->fields++;
resource->bufferFields[0].offset = 0;
resource->bufferFields[0].name = resource->name;
return spv_parse_field(spv, type, resource->bufferFields, info);
} else {
resource->fields = NULL;
resource->bufferFields = NULL;
}
// Sampler and texture variables are named directly
@ -431,39 +461,78 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
return SPV_OK;
}
// Combined image samplers are currently not supported (ty webgpu)
resource->textureFlags = 0;
// Unwrap combined image samplers
if (OP_CODE(type) == 27) { // OpTypeSampledImage
resource->type = SPV_COMBINED_TEXTURE_SAMPLER;
return SPV_OK;
if (!spv_load_type(spv, type[2], &type)) {
return SPV_INVALID;
}
} else if (OP_CODE(type) != 25) { // OpTypeImage
return SPV_INVALID;
}
} else {
// Texel buffers use the DimBuffer dimensionality
if (type[3] == 5) {
resource->dimension = SPV_TEXTURE_1D;
switch (type[7]) {
case 1: resource->type = SPV_UNIFORM_TEXEL_BUFFER; return SPV_OK;
case 2: resource->type = SPV_STORAGE_TEXEL_BUFFER; return SPV_OK;
default: return SPV_INVALID;
}
}
// Texel buffers use the DimBuffer dimensionality
if (type[3] == 5) {
// Input attachments use the DimSubpassData dimensionality, and "Sampled" must be 2
if (type[3] == 6) {
if (type[7] == 2) {
resource->type = SPV_INPUT_ATTACHMENT;
return SPV_OK;
} else {
return SPV_INVALID;
}
}
// Read the Sampled key to determine if it's a sampled image (1) or a storage image (2)
switch (type[7]) {
case 1: resource->type = SPV_UNIFORM_TEXEL_BUFFER; return SPV_OK;
case 2: resource->type = SPV_STORAGE_TEXEL_BUFFER; return SPV_OK;
case 1: resource->type = SPV_SAMPLED_TEXTURE; break;
case 2: resource->type = SPV_STORAGE_TEXTURE; break;
default: return SPV_INVALID;
}
}
// Input attachments use the DimSubpassData dimensionality, and "Sampled" must be 2
if (type[3] == 6) {
if (type[7] == 2) {
resource->type = SPV_INPUT_ATTACHMENT;
return SPV_OK;
} else {
return SPV_INVALID;
}
const uint32_t* texelType = NULL;
if (!spv_load_type(spv, type[2], &texelType)) {
return SPV_INVALID;
}
// Read the Sampled key to determine if it's a sampled image (1) or a storage image (2)
switch (type[7]) {
case 1: resource->type = SPV_SAMPLED_TEXTURE; return SPV_OK;
case 2: resource->type = SPV_STORAGE_TEXTURE; return SPV_OK;
if (OP_CODE(texelType) == 21) { // OpTypeInt
resource->textureFlags |= SPV_TEXTURE_INTEGER;
}
switch (type[3]) {
case 0: resource->dimension = SPV_TEXTURE_1D; break;
case 1: resource->dimension = SPV_TEXTURE_2D; break;
case 2: resource->dimension = SPV_TEXTURE_3D; break;
case 3: resource->dimension = SPV_TEXTURE_2D; resource->textureFlags |= SPV_TEXTURE_CUBE; break;
case 4: resource->dimension = SPV_TEXTURE_2D; break;
case 5: return SPV_INVALID; // Handled above
case 6: return SPV_INVALID; // Handled above
default: return SPV_INVALID;
}
if (type[4] == 1) {
resource->textureFlags |= SPV_TEXTURE_SHADOW;
}
if (type[5] == 1) {
resource->textureFlags |= SPV_TEXTURE_ARRAY;
}
if (type[6] == 1) {
resource->textureFlags |= SPV_TEXTURE_MULTISAMPLE;
}
return SPV_OK;
}
static spv_result spv_parse_field(spv_context* spv, const uint32_t* word, spv_field* field, spv_info* info) {

View File

@ -64,13 +64,29 @@ typedef enum {
SPV_INPUT_ATTACHMENT
} spv_resource_type;
typedef enum {
SPV_TEXTURE_1D,
SPV_TEXTURE_2D,
SPV_TEXTURE_3D
} spv_texture_dimension;
enum {
SPV_TEXTURE_CUBE = (1 << 0),
SPV_TEXTURE_ARRAY = (1 << 1),
SPV_TEXTURE_SHADOW = (1 << 2),
SPV_TEXTURE_MULTISAMPLE = (1 << 3),
SPV_TEXTURE_INTEGER = (1 << 4)
};
typedef struct {
uint32_t set;
uint32_t binding;
const uint32_t* set;
const uint32_t* binding;
const char* name;
uint32_t arraySize;
spv_resource_type type;
uint32_t count;
spv_field* fields;
spv_texture_dimension dimension;
uint32_t textureFlags;
spv_field* bufferFields;
} spv_resource;
typedef struct {

View File

@ -92,35 +92,35 @@ typedef uintmax_t atomic_uintmax_t;
// 7.17.7
#define atomic_store(p, x) __atomic_store(p, x, __ATOMIC_SEQ_CST)
#define atomic_store_explicit __atomic_store
#define atomic_store_explicit __atomic_store_n
#define atomic_store(p, x) atomic_store_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_load(p, x) __atomic_load(p, x, __ATOMIC_SEQ_CST)
#define atomic_load_explicit __atomic_load
#define atomic_load_explicit __atomic_load_n
#define atomic_load(p) atomic_load_explicit(p, __ATOMIC_SEQ_CST)
#define atomic_exchange(p, x) __atomic_exchange(p, x, __ATOMIC_SEQ_CST)
#define atomic_exchange_explicit __atomic_exchange
#define atomic_exchange_explicit __atomic_exchange_n
#define atomic_exchange(p, x) atomic_exchange_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_strong(p, x, y) __atomic_compare_exchange(p, x, y, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_strong_explicit(p, x, y, o1, o2) __atomic_compare_exchange(p, x, y, false, o1, o2)
#define atomic_compare_exchange_strong_explicit(p, x, y, o1, o2) __atomic_compare_exchange_n(p, x, y, false, o1, o2)
#define atomic_compare_exchange_strong(p, x, y) atomic_compare_exchange_strong_explicit(p, x, y, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_weak(p, x, y) __atomic_compare_exchange(p, x, y, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_weak_explicit(p, x, y, o1, o2) __atomic_compare_exchange(p, x, y, true, o1, o2)
#define atomic_compare_exchange_weak_explicit(p, x, y, o1, o2) __atomic_compare_exchange_n(p, x, y, true, o1, o2)
#define atomic_compare_exchange_weak(p, x, y) atomic_compare_exchange_weak_explicit(p, x, y, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_fetch_add(p, x) __atomic_fetch_add(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit __atomic_fetch_add
#define atomic_fetch_add(p, x) atomic_fetch_add_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub(p, x) __atomic_fetch_sub(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit __atomic_fetch_sub
#define atomic_fetch_sub(p, x) atomic_fetch_sub_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_or(p, x) __atomic_fetch_or(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit __atomic_fetch_or
#define atomic_fetch_or(p, x) atomic_fetch_or_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor(p, x) __atomic_fetch_xor(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit __atomic_fetch_xor
#define atomic_fetch_xor(p, x) atomic_fetch_xor_explicit(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_and(p, x) __atomic_fetch_and(p, x, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit __atomic_fetch_and
#define atomic_fetch_and(p, x) atomic_fetch_and_explicit(p, x, __ATOMIC_SEQ_CST)
// 7.17.8
@ -145,8 +145,26 @@ _Bool atomic_flag_clear_explicit(volatile atomic_flag*, memory_order);
typedef volatile long atomic_uint;
#define atomic_store(p, x) *(p) = (x);
#define atomic_store_explicit(p, x, o) atomic_store(p, x)
#define atomic_load(p) *(p)
#define atomic_load_explicit(p, o) atomic_load(p)
#define atomic_fetch_add(p, x) _InterlockedExchangeAdd(p, x)
#define atomic_fetch_add_explicit(p, x, o) atomic_fetch_add(p, x)
#define atomic_fetch_sub(p, x) _InterlockedExchangeAdd(p, -(x))
#define atomic_fetch_sub_explicit(p, x, o) atomic_fetch_sub(p, x)
#define atomic_fetch_or(p, x) InterlockedOr(p, x)
#define atomic_fetch_or_explicit(p, x, o) atomic_fetch_or(p, x)
#define atomic_fetch_xor(p, x) InterlockedXor(p, x)
#define atomic_fetch_xor_explicit(p, x, o) atomic_fetch_xor(p, x)
#define atomic_fetch_and(p, x) InterlockedAnd(p, x)
#define atomic_fetch_and_explicit(p, x, o) atomic_fetch_and(p, x)
#define ATOMIC_INT_LOCK_FREE 2

View File

@ -13,11 +13,13 @@ enum { mtx_plain };
typedef HANDLE thrd_t;
typedef CRITICAL_SECTION mtx_t;
typedef CONDITION_VARIABLE cnd_t;
#define thread_local __declspec(thread)
#else
#include <pthread.h>
typedef pthread_t thrd_t;
typedef pthread_mutex_t mtx_t;
typedef pthread_cond_t cnd_t;
#define thread_local _Thread_local
#endif
static inline int thrd_create(thrd_t* thread, thrd_start_t fn, void* arg);

View File

@ -10,6 +10,12 @@
#include <string.h>
#include <stdlib.h>
static void run(void* T) {
while (luax_resume(T, 0) == LUA_YIELD) {
os_sleep(0.);
}
}
int main(int argc, char** argv) {
os_init();
@ -37,12 +43,8 @@ int main(int argc, char** argv) {
}
lua_State* T = lua_tothread(L, -1);
lovrSetErrorCallback(luax_vthrow, T);
lovrSetLogCallback(luax_vlog, T);
while (luax_resume(T, 0) == LUA_YIELD) {
os_sleep(0.);
}
lovrTry(run, T, luax_vthrow, T);
if (lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart")) {
luax_checkvariant(T, 2, &cookie);

View File

@ -229,7 +229,7 @@ void lovrAudioDestroy(void) {
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
for (size_t i = 0; i < 2; i++) {
ma_device_uninit(&state.devices[i]);
free(state.deviceInfo[i]);
lovrFree(state.deviceInfo[i]);
}
Source* source;
FOREACH_SOURCE(source) lovrRelease(source, lovrSourceDestroy);
@ -267,8 +267,7 @@ bool lovrAudioGetDevice(AudioType type, AudioDevice* device) {
}
if (!state.deviceInfo[type]) {
state.deviceInfo[type] = malloc(sizeof(ma_device_info));
lovrAssert(state.deviceInfo[type], "Out of memory");
state.deviceInfo[type] = lovrMalloc(sizeof(ma_device_info));
}
ma_device_info* info = state.deviceInfo[type];
@ -291,8 +290,8 @@ bool lovrAudioSetDevice(AudioType type, void* id, size_t size, Sound* sink, Audi
lovrRetain(sink);
}
lovrAssert(!sink || lovrSoundGetChannelLayout(sink) != CHANNEL_AMBISONIC, "Ambisonic Sounds cannot be used as sinks");
lovrAssert(!sink || lovrSoundIsStream(sink), "Sinks must be streams");
lovrCheck(!sink || lovrSoundGetChannelLayout(sink) != CHANNEL_AMBISONIC, "Ambisonic Sounds cannot be used as sinks");
lovrCheck(!sink || lovrSoundIsStream(sink), "Sinks must be streams");
ma_device_uninit(&state.devices[type]);
lovrRelease(state.sinks[type], lovrSoundDestroy);
@ -407,9 +406,8 @@ void lovrAudioSetAbsorption(float absorption[3]) {
// Source
Source* lovrSourceCreate(Sound* sound, bool pitchable, bool spatial, uint32_t effects) {
lovrAssert(lovrSoundGetChannelLayout(sound) != CHANNEL_AMBISONIC, "Ambisonic Sources are not currently supported");
Source* source = calloc(1, sizeof(Source));
lovrAssert(source, "Out of memory");
lovrCheck(lovrSoundGetChannelLayout(sound) != CHANNEL_AMBISONIC, "Ambisonic Sources are not currently supported");
Source* source = lovrCalloc(sizeof(Source));
source->ref = 1;
source->index = ~0u;
source->sound = sound;
@ -432,8 +430,7 @@ Source* lovrSourceCreate(Sound* sound, bool pitchable, bool spatial, uint32_t ef
config.allowDynamicSampleRate = pitchable;
if (pitchable || config.formatIn != config.formatOut || config.channelsIn != config.channelsOut || config.sampleRateIn != config.sampleRateOut) {
source->converter = malloc(sizeof(ma_data_converter));
lovrAssert(source->converter, "Out of memory");
source->converter = lovrMalloc(sizeof(ma_data_converter));
ma_result status = ma_data_converter_init(&config, NULL, source->converter);
lovrAssert(status == MA_SUCCESS, "Problem creating Source data converter: %s (%d)", ma_result_description(status), status);
}
@ -442,8 +439,7 @@ Source* lovrSourceCreate(Sound* sound, bool pitchable, bool spatial, uint32_t ef
}
Source* lovrSourceClone(Source* source) {
Source* clone = calloc(1, sizeof(Source));
lovrAssert(clone, "Out of memory");
Source* clone = lovrCalloc(sizeof(Source));
clone->ref = 1;
clone->index = ~0u;
clone->sound = source->sound;
@ -460,8 +456,7 @@ Source* lovrSourceClone(Source* source) {
clone->pitchable = source->pitchable;
clone->spatial = source->spatial;
if (source->converter) {
clone->converter = malloc(sizeof(ma_data_converter));
lovrAssert(clone->converter, "Out of memory");
clone->converter = lovrMalloc(sizeof(ma_data_converter));
ma_data_converter_config config = ma_data_converter_config_init_default();
config.formatIn = source->converter->formatIn;
config.formatOut = source->converter->formatOut;
@ -480,8 +475,8 @@ void lovrSourceDestroy(void* ref) {
Source* source = ref;
lovrRelease(source->sound, lovrSoundDestroy);
ma_data_converter_uninit(source->converter, NULL);
free(source->converter);
free(source);
lovrFree(source->converter);
lovrFree(source);
}
Sound* lovrSourceGetSound(Source* source) {
@ -530,7 +525,7 @@ bool lovrSourceIsLooping(Source* source) {
}
void lovrSourceSetLooping(Source* source, bool loop) {
lovrAssert(loop == false || lovrSoundIsStream(source->sound) == false, "Can't loop streams");
lovrCheck(loop == false || lovrSoundIsStream(source->sound) == false, "Can't loop streams");
source->looping = loop;
}

View File

@ -150,8 +150,7 @@ bool phonon_init(void) {
state.renderingSettings.frameSize = BUFFER_SIZE;
state.renderingSettings.convolutionType = IPL_CONVOLUTIONTYPE_PHONON;
state.scratchpad = malloc(BUFFER_SIZE * 4 * sizeof(float));
if (!state.scratchpad) return phonon_destroy(), false;
state.scratchpad = lovrMalloc(BUFFER_SIZE * 4 * sizeof(float));
IPLHrtfParams hrtfParams = {
.type = IPL_HRTFDATABASETYPE_DEFAULT
@ -167,7 +166,7 @@ bool phonon_init(void) {
}
void phonon_destroy(void) {
if (state.scratchpad) free(state.scratchpad);
if (state.scratchpad) lovrFree(state.scratchpad);
for (size_t i = 0; i < MAX_SOURCES; i++) {
if (state.binauralEffect[i]) phonon_iplDestroyBinauralEffect(&state.binauralEffect[i]);
if (state.directSoundEffect[i]) phonon_iplDestroyDirectSoundEffect(&state.directSoundEffect[i]);
@ -334,8 +333,7 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount
.irradianceMinDistance = .1f
};
IPLint32* triangleMaterials = malloc(indexCount / 3 * sizeof(IPLint32));
if (!triangleMaterials) goto fail;
IPLint32* triangleMaterials = lovrMalloc(indexCount / 3 * sizeof(IPLint32));
for (uint32_t i = 0; i < indexCount / 3; i++) {
triangleMaterials[i] = material;
@ -356,11 +354,11 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount
status = phonon_iplCreateEnvironmentalRenderer(state.context, state.environment, state.renderingSettings, AMBISONIC, NULL, NULL, &state.environmentalRenderer);
if (status != IPL_STATUS_SUCCESS) goto fail;
free(triangleMaterials);
lovrFree(triangleMaterials);
return true;
fail:
free(triangleMaterials);
lovrFree(triangleMaterials);
if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh);
if (state.scene) phonon_iplDestroyScene(&state.scene);
if (state.environment) phonon_iplDestroyEnvironment(&state.environment);

View File

@ -3,8 +3,7 @@
#include <stdlib.h>
Blob* lovrBlobCreate(void* data, size_t size, const char* name) {
Blob* blob = calloc(1, sizeof(Blob));
lovrAssert(blob, "Out of memory");
Blob* blob = lovrMalloc(sizeof(Blob));
blob->ref = 1;
blob->data = data;
blob->size = size;
@ -14,6 +13,6 @@ Blob* lovrBlobCreate(void* data, size_t size, const char* name) {
void lovrBlobDestroy(void* ref) {
Blob* blob = ref;
free(blob->data);
free(blob);
lovrFree(blob->data);
lovrFree(blob);
}

View File

@ -48,6 +48,7 @@ static size_t measure(uint32_t w, uint32_t h, TextureFormat format) {
case FORMAT_RGB10A2: return w * h * 4;
case FORMAT_RG11B10F: return w * h * 4;
case FORMAT_D16: return w * h * 2;
case FORMAT_D24: return w * h * 4;
case FORMAT_D32F: return w * h * 4;
case FORMAT_D24S8: return w * h * 4;
case FORMAT_D32FS8: return w * h * 5;
@ -83,9 +84,8 @@ Image* lovrImageCreateRaw(uint32_t width, uint32_t height, TextureFormat format,
lovrCheck(width > 0 && height > 0, "Image dimensions must be positive");
lovrCheck(format < FORMAT_BC1, "Blank images cannot be compressed");
size_t size = measure(width, height, format);
void* data = malloc(size);
Image* image = calloc(1, sizeof(Image));
lovrAssert(image && data, "Out of memory");
void* data = lovrMalloc(size);
Image* image = lovrCalloc(sizeof(Image));
image->ref = 1;
image->flags = srgb ? IMAGE_SRGB : 0;
image->width = width;
@ -120,7 +120,7 @@ Image* lovrImageCreateFromFile(Blob* blob) {
void lovrImageDestroy(void* ref) {
Image* image = ref;
lovrRelease(image->blob, lovrBlobDestroy);
free(image);
lovrFree(image);
}
bool lovrImageIsSRGB(Image* image) {
@ -138,6 +138,7 @@ bool lovrImageIsCube(Image* image) {
bool lovrImageIsDepth(Image* image) {
switch (image->format) {
case FORMAT_D16:
case FORMAT_D24:
case FORMAT_D32F:
case FORMAT_D24S8:
case FORMAT_D32FS8:
@ -209,7 +210,7 @@ static void setPixelRGBA32F(float* src, ImagePointer dst) { for (uint32_t i = 0;
void lovrImageGetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]) {
lovrCheck(!lovrImageIsCompressed(image), "Unable to access individual pixels of a compressed image");
lovrAssert(x < image->width && y < image->height, "Pixel coordinates must be within Image bounds");
lovrCheck(x < image->width && y < image->height, "Pixel coordinates must be within Image bounds");
size_t offset = measure(y * image->width + x, 1, image->format);
ImagePointer p = { .u8 = (uint8_t*) image->mipmaps[0].data + offset };
switch (image->format) {
@ -228,7 +229,7 @@ void lovrImageGetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]) {
void lovrImageSetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]) {
lovrCheck(!lovrImageIsCompressed(image), "Unable to access individual pixels of a compressed image");
lovrAssert(x < image->width && y < image->height, "Pixel coordinates must be within Image bounds");
lovrCheck(x < image->width && y < image->height, "Pixel coordinates must be within Image bounds");
size_t offset = measure(y * image->width + x, 1, image->format);
ImagePointer p = { .u8 = (uint8_t*) image->mipmaps[0].data + offset };
switch (image->format) {
@ -278,12 +279,12 @@ void lovrImageMapPixel(Image* image, uint32_t x0, uint32_t y0, uint32_t w, uint3
}
void lovrImageCopy(Image* src, Image* dst, uint32_t srcOffset[2], uint32_t dstOffset[2], uint32_t extent[2]) {
lovrAssert(src->format == dst->format, "To copy between Images, their formats must match");
lovrAssert(!lovrImageIsCompressed(src), "Compressed Images cannot be copied");
lovrAssert(dstOffset[0] + extent[0] <= dst->width, "Image copy region extends past the destination image width");
lovrAssert(dstOffset[1] + extent[1] <= dst->height, "Image copy region extends past the destination image height");
lovrAssert(srcOffset[0] + extent[0] <= src->width, "Image copy region extends past the source image width");
lovrAssert(srcOffset[1] + extent[1] <= src->height, "Image copy region extends past the source image height");
lovrCheck(src->format == dst->format, "To copy between Images, their formats must match");
lovrCheck(!lovrImageIsCompressed(src), "Compressed Images cannot be copied");
lovrCheck(dstOffset[0] + extent[0] <= dst->width, "Image copy region extends past the destination image width");
lovrCheck(dstOffset[1] + extent[1] <= dst->height, "Image copy region extends past the destination image height");
lovrCheck(srcOffset[0] + extent[0] <= src->width, "Image copy region extends past the source image width");
lovrCheck(srcOffset[1] + extent[1] <= src->height, "Image copy region extends past the source image height");
size_t pixelSize = measure(1, 1, src->format);
uint8_t* p = (uint8_t*) lovrImageGetLayerData(src, 0, 0) + (srcOffset[1] * src->width + srcOffset[0]) * pixelSize;
uint8_t* q = (uint8_t*) lovrImageGetLayerData(dst, 0, 0) + (dstOffset[1] * dst->width + dstOffset[0]) * pixelSize;
@ -320,7 +321,7 @@ static uint32_t crc32(uint8_t* data, size_t length) {
}
Blob* lovrImageEncode(Image* image) {
lovrAssert(image->format == FORMAT_RGBA8, "Only images with the rgba8 format can be encoded");
lovrCheck(image->format == FORMAT_RGBA8, "Currently, only images with the rgba8 format can be encoded");
uint32_t w = image->width;
uint32_t h = image->height;
uint8_t* pixels = (uint8_t*) image->blob->data;
@ -350,8 +351,7 @@ Blob* lovrImageEncode(Image* image) {
size += 4 + strlen("IHDR") + sizeof(header) + 4;
size += 4 + strlen("IDAT") + idatSize + 4;
size += 4 + strlen("IEND") + 4;
uint8_t* data = malloc(size);
lovrAssert(data, "Out of memory");
uint8_t* data = lovrMalloc(size);
crc_init();
uint32_t crc;
@ -755,7 +755,7 @@ static Image* loadDDS(Blob* blob) {
default: lovrThrow("DDS file uses an unsupported DXGI format (%d)", header10->dxgiFormat);
}
lovrAssert(header10->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE3D, "Loading 3D DDS images is not supported");
lovrCheck(header10->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE3D, "Loading 3D DDS images is not supported");
layers = header10->arraySize;
if (header10->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE) {
@ -787,11 +787,10 @@ static Image* loadDDS(Blob* blob) {
lovrThrow("DDS file uses an unsupported format"); // TODO could handle more uncompressed formats
}
lovrAssert(~header->flags & DDSD_DEPTH, "Loading 3D DDS images is not supported");
lovrCheck(~header->flags & DDSD_DEPTH, "Loading 3D DDS images is not supported");
uint32_t levels = MAX(1, header->mipmapCount);
Image* image = calloc(1, offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
lovrAssert(image, "Out of memory");
Image* image = lovrCalloc(offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
image->ref = 1;
image->flags = flags;
image->width = header->width;
@ -870,8 +869,7 @@ static Image* loadASTC(Blob* blob) {
return NULL;
}
Image* image = calloc(1, sizeof(Image));
lovrAssert(image, "Out of memory");
Image* image = lovrCalloc(sizeof(Image));
image->ref = 1;
image->width = width;
image->height = height;
@ -927,15 +925,14 @@ static Image* loadKTX1(Blob* blob) {
data += header.bytesOfKeyValueData;
length -= header.bytesOfKeyValueData;
lovrAssert(header.pixelWidth > 0, "KTX image dimensions must be positive");
lovrAssert(header.pixelHeight > 0, "Unable to load 1D KTX images");
lovrAssert(header.pixelDepth == 0, "Unable to load 3D KTX images");
lovrCheck(header.pixelWidth > 0, "KTX image dimensions must be positive");
lovrCheck(header.pixelHeight > 0, "Unable to load 1D KTX images");
lovrCheck(header.pixelDepth == 0, "Unable to load 3D KTX images");
uint32_t layers = MAX(header.numberOfArrayElements, 1);
uint32_t levels = MAX(header.numberOfMipmapLevels, 1);
Image* image = calloc(1, offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
lovrAssert(image, "Out of memory");
Image* image = lovrCalloc(offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
image->ref = 1;
image->width = header.pixelWidth;
image->height = header.pixelHeight;
@ -945,8 +942,8 @@ static Image* loadKTX1(Blob* blob) {
lovrRetain(blob);
if (header.numberOfFaces > 1) {
lovrAssert(header.numberOfFaces == 6, "KTX files must have 1 or 6 faces");
lovrAssert(header.numberOfArrayElements == 0, "KTX files with cubemap arrays are not supported");
lovrCheck(header.numberOfFaces == 6, "KTX files must have 1 or 6 faces");
lovrCheck(header.numberOfArrayElements == 0, "KTX files with cubemap arrays are not supported");
image->flags |= IMAGE_CUBEMAP;
image->layers = 6;
}
@ -971,6 +968,7 @@ static Image* loadKTX1(Blob* blob) {
[FORMAT_RGB10A2] = { 0x8368, 0x1908, 0x8059, 0 },
[FORMAT_RG11B10F] = { 0x8C3A, 0x1907, 0x8C3A, 0 },
[FORMAT_D16] = { 0x1403, 0x1902, 0x81A5, 0 },
[FORMAT_D24] = { 0x1405, 0x1902, 0x81A6, 0 },
[FORMAT_D32F] = { 0x1406, 0x1902, 0x8CAC, 0 },
[FORMAT_D24S8] = { 0x84FA, 0x84F9, 0x88F0, 0 },
[FORMAT_D32FS8] = { 0x8DAD, 0x84F9, 0x8CAD, 0 },
@ -1013,7 +1011,7 @@ static Image* loadKTX1(Blob* blob) {
}
}
}
lovrAssert(image->format != ~0u, "KTX1 file uses an unsupported image format (glType = %d, glFormat = %d, glInternalFormat = %d)", header.glType, header.glFormat, header.glInternalFormat);
lovrCheck(image->format != ~0u, "KTX1 file uses an unsupported image format (glType = %d, glFormat = %d, glInternalFormat = %d)", header.glType, header.glFormat, header.glInternalFormat);
// Mipmaps
uint32_t width = image->width;
@ -1074,18 +1072,17 @@ static Image* loadKTX2(Blob* blob) {
return NULL;
}
lovrAssert(header->pixelWidth > 0, "KTX image dimensions must be positive");
lovrAssert(header->pixelHeight > 0, "Unable to load 1D KTX images");
lovrAssert(header->pixelDepth == 0, "Unable to load 3D KTX images");
lovrAssert(header->faceCount == 1 || header->faceCount == 6, "Invalid KTX file (faceCount must be 1 or 6)");
lovrAssert(header->layerCount == 0 || header->faceCount == 1, "Unable to load cubemap array KTX images");
lovrAssert(!header->compression, "Supercompressed KTX files are not currently supported");
lovrCheck(header->pixelWidth > 0, "KTX image dimensions must be positive");
lovrCheck(header->pixelHeight > 0, "Unable to load 1D KTX images");
lovrCheck(header->pixelDepth == 0, "Unable to load 3D KTX images");
lovrCheck(header->faceCount == 1 || header->faceCount == 6, "Invalid KTX file (faceCount must be 1 or 6)");
lovrCheck(header->layerCount == 0 || header->faceCount == 1, "Unable to load cubemap array KTX images");
lovrCheck(!header->compression, "Supercompressed KTX files are not currently supported");
uint32_t layers = MAX(header->layerCount, 1);
uint32_t levels = MAX(header->levelCount, 1);
Image* image = calloc(1, offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
lovrAssert(image, "Out of memory");
Image* image = lovrCalloc(offsetof(Image, mipmaps) + levels * sizeof(Mipmap));
image->ref = 1;
image->width = header->pixelWidth;
image->height = header->pixelHeight;
@ -1118,6 +1115,7 @@ static Image* loadKTX2(Blob* blob) {
case 64: image->format = FORMAT_RGB10A2; break;
case 122: image->format = FORMAT_RG11B10F; break;
case 124: image->format = FORMAT_D16; break;
case 125: image->format = FORMAT_D24; break;
case 126: image->format = FORMAT_D32F; break;
case 129: image->format = FORMAT_D24S8; break;
case 130: image->format = FORMAT_D32FS8; break;
@ -1194,8 +1192,7 @@ static Image* loadSTB(Blob* blob) {
size_t size = measure(width, height, format);
Image* image = calloc(1, sizeof(Image));
lovrAssert(image, "Out of memory");
Image* image = lovrCalloc(sizeof(Image));
image->ref = 1;
image->flags = flags;
image->width = width;

View File

@ -24,6 +24,7 @@ typedef enum {
FORMAT_RGB10A2,
FORMAT_RG11B10F,
FORMAT_D16,
FORMAT_D24,
FORMAT_D32F,
FORMAT_D24S8,
FORMAT_D32FS8,
@ -77,5 +78,4 @@ void lovrImageGetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]);
void lovrImageSetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]);
void lovrImageMapPixel(Image* image, uint32_t x, uint32_t y, uint32_t w, uint32_t h, MapPixelCallback* callback, void* userdata);
void lovrImageCopy(Image* src, Image* dst, uint32_t srcOffset[2], uint32_t dstOffset[2], uint32_t extent[2]);
void lovrImageClear(Image* image);
struct Blob* lovrImageEncode(Image* image);

View File

@ -12,7 +12,8 @@ static size_t typeSizes[] = {
[U16] = 2,
[I32] = 4,
[U32] = 4,
[F32] = 4
[F32] = 4,
[SN10x3] = 4
};
static void* nullIO(const char* path, size_t* count) {
@ -20,8 +21,7 @@ static void* nullIO(const char* path, size_t* count) {
}
ModelData* lovrModelDataCreate(Blob* source, ModelDataIO* io) {
ModelData* model = calloc(1, sizeof(ModelData));
lovrAssert(model, "Out of memory");
ModelData* model = lovrCalloc(sizeof(ModelData));
model->ref = 1;
if (!io) {
@ -54,11 +54,11 @@ void lovrModelDataDestroy(void* ref) {
map_free(&model->animationMap);
map_free(&model->materialMap);
map_free(&model->nodeMap);
free(model->vertices);
free(model->indices);
free(model->metadata);
free(model->data);
free(model);
lovrFree(model->vertices);
lovrFree(model->indices);
lovrFree(model->metadata);
lovrFree(model->data);
lovrFree(model);
}
// Batches allocations for all the ModelData arrays
@ -83,8 +83,7 @@ void lovrModelDataAllocate(ModelData* model) {
totalSize += sizes[14] = model->charCount * sizeof(char);
size_t offset = 0;
char* p = model->data = calloc(1, totalSize);
lovrAssert(model->data, "Out of memory");
char* p = model->data = lovrCalloc(totalSize);
model->blobs = (Blob**) (p + offset), offset += sizes[0];
model->buffers = (ModelBuffer*) (p + offset), offset += sizes[1];
model->images = (Image**) (p + offset), offset += sizes[2];
@ -243,6 +242,22 @@ void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char
} else {
lovrUnreachable();
}
} else if (type == SN10x3) {
if (attribute->type == F32) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
float x = ((float*) src)[0];
float y = ((float*) src)[1];
float z = ((float*) src)[2];
float w = attribute->components == 4 ? ((float*) src)[3] : 0.f;
*(uint32_t*) dst =
((((uint32_t) (int32_t) (x * 511.f)) & 0x3ff) << 0) |
((((uint32_t) (int32_t) (y * 511.f)) & 0x3ff) << 10) |
((((uint32_t) (int32_t) (z * 511.f)) & 0x3ff) << 20) |
((((uint32_t) (int32_t) (w * 2.f)) & 0x003) << 30);
}
} else {
lovrUnreachable();
}
} else {
lovrUnreachable();
}
@ -372,8 +387,7 @@ void lovrModelDataGetBoundingSphere(ModelData* model, float sphere[4]) {
}
uint32_t pointCount = totalPrimitiveCount * 8;
float* points = malloc(pointCount * 3 * sizeof(float));
lovrAssert(points, "Out of memory");
float* points = lovrMalloc(pointCount * 3 * sizeof(float));
uint32_t pointIndex = 0;
boundingSphereHelper(model, model->rootNode, &pointIndex, points, (float[16]) MAT4_IDENTITY);
@ -428,7 +442,7 @@ void lovrModelDataGetBoundingSphere(ModelData* model, float sphere[4]) {
model->boundingSphere[1] = y;
model->boundingSphere[2] = z;
model->boundingSphere[3] = r;
free(points);
lovrFree(points);
}
memcpy(sphere, model->boundingSphere, sizeof(model->boundingSphere));
@ -513,7 +527,7 @@ static void collectVertices(ModelData* model, uint32_t nodeIndex, float** vertic
*baseIndex += positions->count;
}
if (index) {
if (indices && index) {
lovrAssert(index->type == U16 || index->type == U32, "Unreachable");
char* data = (char*) model->buffers[index->buffer].data + index->offset;
@ -544,20 +558,18 @@ void lovrModelDataGetTriangles(ModelData* model, float** vertices, uint32_t** in
}
if (vertices && !model->vertices) {
uint32_t* tempIndices;
uint32_t baseIndex = 0;
model->vertices = malloc(model->totalVertexCount * 3 * sizeof(float));
model->indices = malloc(model->totalIndexCount * sizeof(uint32_t));
lovrAssert(model->vertices && model->indices, "Out of memory");
model->vertices = lovrMalloc(model->totalVertexCount * 3 * sizeof(float));
model->indices = lovrMalloc(model->totalIndexCount * sizeof(uint32_t));
*vertices = model->vertices;
*indices = model->indices;
collectVertices(model, model->rootNode, vertices, indices, &baseIndex, (float[16]) MAT4_IDENTITY);
tempIndices = model->indices;
collectVertices(model, model->rootNode, vertices, &tempIndices, &baseIndex, (float[16]) MAT4_IDENTITY);
}
*vertexCount = model->totalVertexCount;
*indexCount = model->totalIndexCount;
if (vertexCount) *vertexCount = model->totalVertexCount;
if (indexCount) *indexCount = model->totalIndexCount;
if (vertices) {
*vertices = model->vertices;
*indices = model->indices;
}
if (vertices) *vertices = model->vertices;
if (indices) *indices = model->indices;
}

View File

@ -33,7 +33,7 @@ typedef enum {
MAX_DEFAULT_ATTRIBUTES
} DefaultAttribute;
typedef enum { I8, U8, I16, U16, I32, U32, F32 } AttributeType;
typedef enum { I8, U8, I16, U16, I32, U32, F32, SN10x3 } AttributeType;
typedef union {
void* raw;

View File

@ -88,10 +88,7 @@ static void* decodeBase64(char* str, size_t length, size_t* decodedLength) {
length -= s - str;
int padding = (s[length - 1] == '=') + (s[length - 2] == '=');
*decodedLength = length / 4 * 3 - padding;
uint8_t* data = malloc(*decodedLength);
if (!data) {
return NULL;
}
uint8_t* data = lovrMalloc(*decodedLength);
uint32_t num = 0;
uint32_t bits = 0;
@ -113,7 +110,7 @@ static void* decodeBase64(char* str, size_t length, size_t* decodedLength) {
} else if (c == '=') {
break;
} else {
free(data);
lovrFree(data);
return NULL;
}
@ -224,11 +221,16 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
json = (char*) &jsonHeader[1];
jsonLength = jsonHeader->length;
gltfChunkHeader* binHeader = (gltfChunkHeader*) &json[jsonLength];
lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header");
if (source->size > sizeof(gltfHeader) + sizeof(gltfChunkHeader) + jsonLength + 4) {
gltfChunkHeader* binHeader = (gltfChunkHeader*) &json[jsonLength];
lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header");
binData = (char*) &binHeader[1];
binOffset = (char*) binData - (char*) source->data;
binData = (char*) &binHeader[1];
binOffset = (char*) binData - (char*) source->data;
} else {
binData = NULL;
binOffset = 0;
}
} else {
json = (char*) data;
jsonLength = source->size;
@ -236,8 +238,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
binOffset = 0;
}
model->metadata = malloc(jsonLength);
lovrAssert(model->metadata, "Out of memory");
model->metadata = lovrMalloc(jsonLength);
memcpy(model->metadata, json, jsonLength);
model->metadataSize = jsonLength;
model->metadataType = META_GLTF_JSON;
@ -266,7 +267,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
}
if (tokenCount <= 0 || tokens[0].type != JSMN_OBJECT) {
free(heapTokens);
lovrFree(heapTokens);
return NULL;
}
@ -320,8 +321,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
}
}
animationSamplers = malloc(samplerCount * sizeof(gltfAnimationSampler));
lovrAssert(animationSamplers, "Out of memory");
animationSamplers = lovrMalloc(samplerCount * sizeof(gltfAnimationSampler));
gltfAnimationSampler* sampler = animationSamplers;
for (int i = (token++)->size; i > 0; i--) {
for (int k = (token++)->size; k > 0; k--) {
@ -364,8 +364,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
} else if (STR_EQ(key, "images")) {
model->imageCount = token->size;
images = malloc(model->imageCount * sizeof(gltfImage));
lovrAssert(images, "Out of memory");
images = lovrMalloc(model->imageCount * sizeof(gltfImage));
gltfImage* image = images;
for (int i = (token++)->size; i > 0; i--, image++) {
image->bufferView = ~0u;
@ -383,8 +382,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
}
} else if (STR_EQ(key, "textures")) {
textures = malloc(token->size * sizeof(gltfTexture));
lovrAssert(textures, "Out of memory");
textures = lovrMalloc(token->size * sizeof(gltfTexture));
gltfTexture* texture = textures;
for (int i = (token++)->size; i > 0; i--, texture++) {
texture->image = ~0u;
@ -428,8 +426,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
} else if (STR_EQ(key, "meshes")) {
info.meshes = token;
meshes = malloc(token->size * sizeof(gltfMesh));
lovrAssert(meshes, "Out of memory");
meshes = lovrMalloc(token->size * sizeof(gltfMesh));
gltfMesh* mesh = meshes;
model->primitiveCount = 0;
model->blendShapeCount = 0;
@ -495,8 +492,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
} else if (STR_EQ(key, "scenes")) {
info.scenes = token;
info.sceneCount = token->size;
scenes = malloc(info.sceneCount * sizeof(gltfScene));
lovrAssert(scenes, "Out of memory");
scenes = lovrMalloc(info.sceneCount * sizeof(gltfScene));
gltfScene* scene = scenes;
for (int i = (token++)->size; i > 0; i--, scene++) {
for (int k = (token++)->size; k > 0; k--) {
@ -1053,11 +1049,11 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
model->rootNode = scenes[rootScene].node;
}
free(animationSamplers);
free(meshes);
free(images);
free(textures);
free(scenes);
free(heapTokens);
lovrFree(animationSamplers);
lovrFree(meshes);
lovrFree(images);
lovrFree(textures);
lovrFree(scenes);
lovrFree(heapTokens);
return model;
}

View File

@ -101,7 +101,7 @@ static void parseMtl(char* path, char* base, ModelDataIO* io, arr_image_t* image
data = newline + 1;
}
free(p);
lovrFree(p);
}
ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io) {
@ -123,16 +123,16 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io)
arr_t(float) normals;
arr_t(float) uvs;
arr_init(&groups, arr_alloc);
arr_init(&images, arr_alloc);
arr_init(&materials, arr_alloc);
arr_init(&groups);
arr_init(&images);
arr_init(&materials);
map_init(&materialMap, 0);
arr_init(&vertexBlob, arr_alloc);
arr_init(&indexBlob, arr_alloc);
arr_init(&vertexBlob);
arr_init(&indexBlob);
map_init(&vertexMap, 0);
arr_init(&positions, arr_alloc);
arr_init(&normals, arr_alloc);
arr_init(&uvs, arr_alloc);
arr_init(&positions);
arr_init(&normals);
arr_init(&uvs);
arr_push(&groups, ((objGroup) { .material = -1 }));
@ -197,7 +197,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io)
// Triangulate faces (triangle fan)
if (i >= 3) {
arr_push(&indexBlob, indexBlob.data[indexBlob.length - i]);
arr_push(&indexBlob, indexBlob.data[indexBlob.length - (3 * (i - 2))]);
arr_push(&indexBlob, indexBlob.data[indexBlob.length - 2]);
group->count += 2;
}

View File

@ -17,8 +17,7 @@ static ModelData* lovrModelDataInitStlBinary(ModelData* model, Blob* source, Mod
uint32_t vertexCount = triangleCount * 3;
size_t vertexBufferSize = vertexCount * 6 * sizeof(float);
float* vertices = malloc(vertexBufferSize);
lovrAssert(vertices, "Out of memory");
float* vertices = lovrMalloc(vertexBufferSize);
model->blobCount = 1;
model->bufferCount = 1;

View File

@ -19,8 +19,7 @@ struct Rasterizer {
};
Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
Rasterizer* rasterizer = calloc(1, sizeof(Rasterizer));
lovrAssert(rasterizer, "Out of memory");
Rasterizer* rasterizer = lovrCalloc(sizeof(Rasterizer));
rasterizer->ref = 1;
stbtt_fontinfo* font = &rasterizer->font;
@ -47,7 +46,7 @@ Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
void lovrRasterizerDestroy(void* ref) {
Rasterizer* rasterizer = ref;
lovrRelease(rasterizer->blob, lovrBlobDestroy);
free(rasterizer);
lovrFree(rasterizer);
}
float lovrRasterizerGetFontSize(Rasterizer* rasterizer) {

View File

@ -78,8 +78,7 @@ static uint32_t lovrSoundReadMp3(Sound* sound, uint32_t offset, uint32_t count,
// Sound
Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, ChannelLayout layout, uint32_t sampleRate, Blob* blob) {
Sound* sound = calloc(1, sizeof(Sound));
lovrAssert(sound, "Out of memory");
Sound* sound = lovrCalloc(sizeof(Sound));
sound->ref = 1;
sound->frames = frames;
sound->format = format;
@ -87,8 +86,7 @@ Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, ChannelLayout la
sound->sampleRate = sampleRate;
sound->read = lovrSoundReadRaw;
size_t size = frames * lovrSoundGetStride(sound);
void* data = calloc(1, size);
lovrAssert(data, "Out of memory");
void* data = lovrCalloc(size);
sound->blob = lovrBlobCreate(data, size, "Sound");
if (blob) {
@ -99,19 +97,16 @@ Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, ChannelLayout la
}
Sound* lovrSoundCreateStream(uint32_t frames, SampleFormat format, ChannelLayout layout, uint32_t sampleRate) {
Sound* sound = calloc(1, sizeof(Sound));
lovrAssert(sound, "Out of memory");
Sound* sound = lovrCalloc(sizeof(Sound));
sound->ref = 1;
sound->frames = frames;
sound->format = format;
sound->layout = layout;
sound->sampleRate = sampleRate;
sound->read = lovrSoundReadStream;
sound->stream = malloc(sizeof(ma_pcm_rb));
lovrAssert(sound->stream, "Out of memory");
sound->stream = lovrMalloc(sizeof(ma_pcm_rb));
size_t size = frames * lovrSoundGetStride(sound);
void* data = malloc(size);
lovrAssert(data, "Out of memory");
void* data = lovrMalloc(size);
sound->blob = lovrBlobCreate(data, size, NULL);
ma_result status = ma_pcm_rb_init(miniaudioFormats[format], lovrSoundGetChannelCount(sound), frames, data, NULL, sound->stream);
lovrAssert(status == MA_SUCCESS, "Failed to create ring buffer for streamed Sound: %s (%d)", ma_result_description(status), status);
@ -135,8 +130,7 @@ static bool loadOgg(Sound* sound, Blob* blob, bool decode) {
uint32_t channels = lovrSoundGetChannelCount(sound);
lovrAssert(sound->frames * channels <= INT_MAX, "Decoded OGG file has too many samples");
size_t size = sound->frames * lovrSoundGetStride(sound);
void* data = calloc(1, size);
lovrAssert(data, "Out of memory");
void* data = lovrCalloc(size);
sound->blob = lovrBlobCreate(data, size, "Sound");
if (stb_vorbis_get_samples_float_interleaved(sound->decoder, channels, data, (int) size / sizeof(float)) < (int) sound->frames) {
lovrThrow("Could not decode vorbis from '%s'", blob->name);
@ -229,8 +223,7 @@ static bool loadWAV(Sound* sound, Blob* blob, bool decode) {
// Conversion
size_t samples = sound->frames * lovrSoundGetChannelCount(sound);
size_t bytes = sound->frames * lovrSoundGetStride(sound);
void* raw = malloc(bytes);
lovrAssert(raw, "Out of memory");
void* raw = lovrMalloc(bytes);
if (pcm && wav->sampleSize == 24) {
float* out = raw;
const uint8_t* in = (const uint8_t*) data;
@ -296,10 +289,9 @@ static bool loadMP3(Sound* sound, Blob* blob, bool decode) {
sound->read = lovrSoundReadRaw;
return true;
} else {
mp3dec_ex_t* decoder = sound->decoder = malloc(sizeof(mp3dec_ex_t));
lovrAssert(decoder, "Out of memory");
mp3dec_ex_t* decoder = sound->decoder = lovrMalloc(sizeof(mp3dec_ex_t));
if (mp3dec_ex_open_buf(sound->decoder, blob->data, blob->size, MP3D_SEEK_TO_SAMPLE)) {
free(sound->decoder);
lovrFree(sound->decoder);
lovrThrow("Could not load mp3 from '%s'", blob->name);
}
sound->format = SAMPLE_F32;
@ -314,8 +306,7 @@ static bool loadMP3(Sound* sound, Blob* blob, bool decode) {
}
Sound* lovrSoundCreateFromFile(Blob* blob, bool decode) {
Sound* sound = calloc(1, sizeof(Sound));
lovrAssert(sound, "Out of memory");
Sound* sound = lovrCalloc(sizeof(Sound));
sound->ref = 1;
if (loadOgg(sound, blob, decode)) return sound;
@ -326,8 +317,7 @@ Sound* lovrSoundCreateFromFile(Blob* blob, bool decode) {
}
Sound* lovrSoundCreateFromCallback(SoundCallback read, void *callbackMemo, SoundDestroyCallback callbackMemoDestroy, SampleFormat format, uint32_t sampleRate, ChannelLayout layout, uint32_t maxFrames) {
Sound* sound = calloc(1, sizeof(Sound));
lovrAssert(sound, "Out of memory");
Sound* sound = lovrCalloc(sizeof(Sound));
sound->ref = 1;
sound->read = read;
sound->format = format;
@ -344,10 +334,10 @@ void lovrSoundDestroy(void* ref) {
if (sound->callbackMemoDestroy) sound->callbackMemoDestroy(sound);
lovrRelease(sound->blob, lovrBlobDestroy);
if (sound->read == lovrSoundReadOgg) stb_vorbis_close(sound->decoder);
if (sound->read == lovrSoundReadMp3) mp3dec_ex_close(sound->decoder), free(sound->decoder);
if (sound->read == lovrSoundReadMp3) mp3dec_ex_close(sound->decoder), lovrFree(sound->decoder);
ma_pcm_rb_uninit(sound->stream);
free(sound->stream);
free(sound);
lovrFree(sound->stream);
lovrFree(sound);
}
Blob* lovrSoundGetBlob(Sound* sound) {
@ -395,8 +385,8 @@ uint32_t lovrSoundRead(Sound* sound, uint32_t offset, uint32_t count, void* data
}
uint32_t lovrSoundWrite(Sound* sound, uint32_t offset, uint32_t count, const void* data) {
lovrAssert(!sound->decoder, "Compressed Sound can not be written to");
lovrAssert(sound->stream || sound->blob, "Live-generated sound can not be written to");
lovrCheck(!sound->decoder, "Compressed Sound can not be written to");
lovrCheck(sound->stream || sound->blob, "Live-generated sound can not be written to");
size_t stride = lovrSoundGetStride(sound);
uint32_t frames = 0;
@ -422,11 +412,11 @@ uint32_t lovrSoundWrite(Sound* sound, uint32_t offset, uint32_t count, const voi
}
uint32_t lovrSoundCopy(Sound* src, Sound* dst, uint32_t count, uint32_t srcOffset, uint32_t dstOffset) {
lovrAssert(!dst->decoder, "Compressed Sound can not be written to");
lovrAssert(dst->stream || dst->blob, "Live-generated sound can not be written to");
lovrAssert(src != dst, "Can not copy a Sound to itself");
lovrAssert(src->format == dst->format, "Sound formats need to match");
lovrAssert(src->layout == dst->layout, "Sound channel layouts need to match");
lovrCheck(!dst->decoder, "Compressed Sound can not be written to");
lovrCheck(dst->stream || dst->blob, "Live-generated sound can not be written to");
lovrCheck(src != dst, "Can not copy a Sound to itself");
lovrCheck(src->format == dst->format, "Sound formats need to match");
lovrCheck(src->layout == dst->layout, "Sound channel layouts need to match");
uint32_t frames = 0;
if (dst->stream) {

View File

@ -13,16 +13,16 @@ static struct {
void lovrVariantDestroy(Variant* variant) {
switch (variant->type) {
case TYPE_STRING: free(variant->value.string.pointer); return;
case TYPE_STRING: lovrFree(variant->value.string.pointer); return;
case TYPE_OBJECT: lovrRelease(variant->value.object.pointer, variant->value.object.destructor); return;
case TYPE_MATRIX: free(variant->value.matrix.data); return;
case TYPE_MATRIX: lovrFree(variant->value.matrix.data); return;
default: return;
}
}
bool lovrEventInit(void) {
if (atomic_fetch_add(&state.ref, 1)) return false;
arr_init(&state.events, arr_alloc);
arr_init(&state.events);
return true;
}
@ -51,8 +51,7 @@ void lovrEventPush(Event event) {
if (event.type == EVENT_THREAD_ERROR) {
lovrRetain(event.data.thread.thread);
size_t length = strlen(event.data.thread.error);
char* copy = malloc(length + 1);
lovrAssert(copy, "Out of memory");
char* copy = lovrMalloc(length + 1);
memcpy(copy, event.data.thread.error, length);
copy[length] = '\0';
event.data.thread.error = copy;

View File

@ -179,9 +179,9 @@ void lovrFilesystemDestroy(void) {
}
void lovrFilesystemSetSource(const char* source) {
lovrAssert(!state.source[0], "Source is already set!");
lovrCheck(!state.source[0], "Source is already set!");
size_t length = strlen(source);
lovrAssert(sizeof(state.source) > length, "Source is too long!");
lovrCheck(sizeof(state.source) > length, "Source is too long!");
memcpy(state.source, source, length);
state.source[length] = '\0';
}
@ -310,8 +310,7 @@ void* lovrFilesystemRead(const char* p, size_t* size) {
}
*size = (size_t) bytes;
void* data = malloc(*size);
lovrAssert(data, "Out of memory");
void* data = lovrMalloc(*size);
if (archive->read(archive, &handle, data, *size, size)) {
archive->close(archive, &handle);
@ -569,7 +568,7 @@ static bool zip_init(Archive* archive, const char* filename, const char* root) {
// Parse the number of file entries and reserve memory
uint16_t nodeCount = readu16(p + 10);
arr_init(&archive->nodes, realloc);
arr_init(&archive->nodes);
arr_reserve(&archive->nodes, nodeCount);
map_init(&archive->lookup, nodeCount);
@ -737,8 +736,7 @@ static bool zip_open(Archive* archive, const char* path, Handle* handle) {
}
if (handle->node->compressed) {
zip_stream* stream = handle->stream = malloc(sizeof(zip_stream));
lovrAssert(stream, "Out of memory");
zip_stream* stream = handle->stream = lovrMalloc(sizeof(zip_stream));
tinfl_init(&stream->decompressor);
stream->inputCursor = 0;
stream->outputCursor = 0;
@ -752,7 +750,7 @@ static bool zip_open(Archive* archive, const char* path, Handle* handle) {
}
static bool zip_close(Archive* archive, Handle* handle) {
free(handle->stream);
lovrFree(handle->stream);
return true;
}
@ -906,8 +904,7 @@ static void zip_list(Archive* archive, const char* path, fs_list_cb callback, vo
// Archive
Archive* lovrArchiveCreate(const char* path, const char* mountpoint, const char* root) {
Archive* archive = calloc(1, sizeof(Archive));
lovrAssert(archive, "Out of memory");
Archive* archive = lovrCalloc(sizeof(Archive));
archive->ref = 1;
if (dir_init(archive, path, root)) {
@ -927,20 +924,18 @@ Archive* lovrArchiveCreate(const char* path, const char* mountpoint, const char*
archive->stat = zip_stat;
archive->list = zip_list;
} else {
free(archive);
lovrFree(archive);
return NULL;
}
if (mountpoint) {
size_t length = strlen(mountpoint);
archive->mountpoint = malloc(length + 1);
lovrAssert(archive->mountpoint, "Out of memory");
archive->mountpoint = lovrMalloc(length + 1);
archive->mountLength = normalize(mountpoint, length, archive->mountpoint);
}
archive->pathLength = strlen(path);
archive->path = malloc(archive->pathLength + 1);
lovrAssert(archive->path, "Out of memory");
archive->path = lovrMalloc(archive->pathLength + 1);
memcpy(archive->path, path, archive->pathLength + 1);
return archive;
@ -949,9 +944,9 @@ Archive* lovrArchiveCreate(const char* path, const char* mountpoint, const char*
void lovrArchiveDestroy(void* ref) {
Archive* archive = ref;
if (archive->data) zip_free(archive);
free(archive->mountpoint);
free(archive->path);
free(archive);
lovrFree(archive->mountpoint);
lovrFree(archive->path);
lovrFree(archive);
}
// File
@ -990,16 +985,14 @@ File* lovrFileCreate(const char* p, OpenMode mode, const char** error) {
}
}
File* file = calloc(1, sizeof(File));
lovrAssert(file, "Out of memory");
File* file = lovrCalloc(sizeof(File));
file->ref = 1;
file->mode = mode;
file->handle = handle;
file->archive = archive;
lovrRetain(archive);
file->path = malloc(length + 1);
lovrAssert(file->path, "Out of memory");
file->path = lovrMalloc(length + 1);
memcpy(file->path, path, length + 1);
return file;
@ -1009,8 +1002,8 @@ void lovrFileDestroy(void* ref) {
File* file = ref;
if (file->archive) file->archive->close(file->archive, &file->handle);
lovrRelease(file->archive, lovrArchiveDestroy);
free(file->path);
free(file);
lovrFree(file->path);
lovrFree(file);
}
const char* lovrFileGetPath(File* file) {

File diff suppressed because it is too large Load Diff

View File

@ -116,6 +116,7 @@ typedef enum {
TYPE_U8x4,
TYPE_SN8x4,
TYPE_UN8x4,
TYPE_SN10x3,
TYPE_UN10x3,
TYPE_I16,
TYPE_I16x2,
@ -201,32 +202,30 @@ enum {
};
typedef struct {
Texture* parent;
TextureType type;
uint32_t layerIndex;
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
} TextureViewInfo;
typedef struct {
Texture* parent;
TextureType type;
uint32_t format;
uint32_t width;
uint32_t height;
uint32_t layers;
uint32_t mipmaps;
uint32_t samples;
uint32_t usage;
bool srgb;
bool xr;
uintptr_t handle;
uint32_t imageCount;
struct Image** images;
const char* label;
uintptr_t handle;
} TextureInfo;
typedef struct {
TextureType type;
uint32_t layerIndex;
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
const char* label;
} TextureViewInfo;
typedef enum {
FILTER_NEAREST,
FILTER_LINEAR
@ -234,7 +233,7 @@ typedef enum {
Texture* lovrGraphicsGetWindowTexture(void);
Texture* lovrTextureCreate(const TextureInfo* info);
Texture* lovrTextureCreateView(const TextureViewInfo* view);
Texture* lovrTextureCreateView(Texture* parent, const TextureViewInfo* info);
void lovrTextureDestroy(void* ref);
const TextureInfo* lovrTextureGetInfo(Texture* texture);
struct Image* lovrTextureGetPixels(Texture* texture, uint32_t offset[4], uint32_t extent[3]);
@ -286,21 +285,25 @@ typedef enum {
SHADER_EQUIRECT,
SHADER_FILL_2D,
SHADER_FILL_ARRAY,
SHADER_LOGO,
SHADER_ANIMATOR,
SHADER_BLENDER,
SHADER_TALLY_MERGE,
DEFAULT_SHADER_COUNT
} DefaultShader;
typedef enum {
SHADER_GRAPHICS,
SHADER_COMPUTE
} ShaderType;
typedef enum {
STAGE_VERTEX,
STAGE_FRAGMENT,
STAGE_COMPUTE,
STAGE_COUNT
STAGE_COMPUTE
} ShaderStage;
typedef struct {
ShaderStage stage;
const void* code;
size_t size;
} ShaderSource;
@ -312,15 +315,18 @@ typedef struct {
} ShaderFlag;
typedef struct {
ShaderSource source[STAGE_COUNT];
uint32_t flagCount;
ShaderType type;
ShaderSource* stages;
uint32_t stageCount;
ShaderFlag* flags;
uint32_t flagCount;
const char* label;
bool isDefault;
} ShaderInfo;
typedef void* ShaderIncluder(const char* filename, size_t* bytesRead);
ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source, ShaderIncluder* includer);
void lovrGraphicsCompileShader(ShaderSource* stages, ShaderSource* outputs, uint32_t count, ShaderIncluder* includer);
ShaderSource lovrGraphicsGetDefaultShaderSource(DefaultShader type, ShaderStage stage);
Shader* lovrGraphicsGetDefaultShader(DefaultShader type);
Shader* lovrShaderCreate(const ShaderInfo* info);
@ -607,10 +613,10 @@ void lovrPassSetViewCull(Pass* pass, bool enable);
void lovrPassSetWinding(Pass* pass, Winding winding);
void lovrPassSetWireframe(Pass* pass, bool wireframe);
void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t slot, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t slot, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t slot, Sampler* sampler);
void lovrPassSendData(Pass* pass, const char* name, size_t length, uint32_t slot, void** data, DataField** format);
void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassSendTexture(Pass* pass, const char* name, size_t length, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, Sampler* sampler);
void lovrPassSendData(Pass* pass, const char* name, size_t length, void** data, DataField** format);
void lovrPassPoints(Pass* pass, uint32_t count, float** vertices);
void lovrPassLine(Pass* pass, uint32_t count, float** vertices);

View File

@ -301,12 +301,6 @@ static void createReferenceSpace(XrTime time) {
return;
}
char name[256];
if (openxr_getDriverName(name, sizeof(name)) && !memcmp(name, "SteamVR", strlen("SteamVR"))) {
state.referenceSpace = state.spaces[DEVICE_FLOOR];
return;
}
if (state.features.localFloor) {
info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
} else if (state.config.seated) {
@ -439,6 +433,7 @@ static void swapchain_init(Swapchain* swapchain, uint32_t width, uint32_t height
if (depth) {
info.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
switch (state.depthFormat) {
case FORMAT_D24: info.format = VK_FORMAT_X8_D24_UNORM_PACK32; break;
case FORMAT_D32F: info.format = VK_FORMAT_D32_SFLOAT; break;
case FORMAT_D24S8: info.format = VK_FORMAT_D24_UNORM_S8_UINT; break;
case FORMAT_D32FS8: info.format = VK_FORMAT_D32_SFLOAT_S8_UINT; break;
@ -472,7 +467,6 @@ static void swapchain_init(Swapchain* swapchain, uint32_t width, uint32_t height
.height = height,
.layers = 1 << stereo,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | (depth ? 0 : TEXTURE_SAMPLE),
.handle = (uintptr_t) images[i].image,
.label = "OpenXR Swapchain",
@ -588,8 +582,7 @@ static bool openxr_init(HeadsetConfig* config) {
XR_INIT(result, "Failed to query extensions");
}
XrExtensionProperties* extensionProperties = calloc(extensionCount, sizeof(*extensionProperties));
lovrAssert(extensionProperties, "Out of memory");
XrExtensionProperties* extensionProperties = lovrCalloc(extensionCount * sizeof(*extensionProperties));
for (uint32_t i = 0; i < extensionCount; i++) extensionProperties[i].type = XR_TYPE_EXTENSION_PROPERTIES;
xrEnumerateInstanceExtensionProperties(NULL, extensionCount, &extensionCount, extensionProperties);
@ -638,7 +631,7 @@ static bool openxr_init(HeadsetConfig* config) {
}
}
free(extensionProperties);
lovrFree(extensionProperties);
#ifdef __ANDROID__
XrInstanceCreateInfoAndroidKHR androidInfo = {
@ -732,8 +725,7 @@ static bool openxr_init(HeadsetConfig* config) {
// Blend modes
XR_INIT(xrEnumerateEnvironmentBlendModes(state.instance, state.system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &state.blendModeCount, NULL), "Failed to query blend modes");
state.blendModes = malloc(state.blendModeCount * sizeof(XrEnvironmentBlendMode));
lovrAssert(state.blendModes, "Out of memory");
state.blendModes = lovrMalloc(state.blendModeCount * sizeof(XrEnvironmentBlendMode));
XR_INIT(xrEnumerateEnvironmentBlendModes(state.instance, state.system, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, state.blendModeCount, &state.blendModeCount, state.blendModes), "Failed to query blend modes");
state.blendMode = state.blendModes[0];
}
@ -1375,8 +1367,8 @@ static void openxr_start(void) {
if (hasGraphics) {
state.depthFormat = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F;
if (state.config.stencil && !lovrGraphicsGetFormatSupport(state.depthFormat, TEXTURE_FEATURE_RENDER)) {
state.depthFormat = FORMAT_D24S8; // Guaranteed to be supported if the other one isn't
if (!lovrGraphicsGetFormatSupport(state.depthFormat, TEXTURE_FEATURE_RENDER)) {
state.depthFormat = state.config.stencil ? FORMAT_D24S8 : FORMAT_D24;
}
state.pass = lovrPassCreate();
@ -1386,6 +1378,7 @@ static void openxr_start(void) {
int64_t nativeDepthFormat;
switch (state.depthFormat) {
case FORMAT_D24: nativeDepthFormat = VK_FORMAT_X8_D24_UNORM_PACK32; break;
case FORMAT_D32F: nativeDepthFormat = VK_FORMAT_D32_SFLOAT; break;
case FORMAT_D24S8: nativeDepthFormat = VK_FORMAT_D24_UNORM_S8_UINT; break;
case FORMAT_D32FS8: nativeDepthFormat = VK_FORMAT_D32_SFLOAT_S8_UINT; break;
@ -1475,8 +1468,7 @@ static void openxr_start(void) {
if (state.features.refreshRate) {
XR(xrEnumerateDisplayRefreshRatesFB(state.session, 0, &state.refreshRateCount, NULL), "Failed to query refresh rates");
state.refreshRates = malloc(state.refreshRateCount * sizeof(float));
lovrAssert(state.refreshRates, "Out of memory");
state.refreshRates = lovrMalloc(state.refreshRateCount * sizeof(float));
XR(xrEnumerateDisplayRefreshRatesFB(state.session, state.refreshRateCount, &state.refreshRateCount, state.refreshRates), "Failed to query refresh rates");
}
}
@ -2097,8 +2089,7 @@ static ModelData* openxr_newModelDataFB(XrHandTrackerEXT tracker, bool animated)
totalSize += sizes[9] = ALIGN(jointCount * 16 * sizeof(float), alignment);
// Allocate
char* meshData = malloc(totalSize);
if (!meshData) return NULL;
char* meshData = lovrMalloc(totalSize);
// Write offseted pointers to the mesh struct, to be filled in by the second call
size_t offset = 0;
@ -2117,12 +2108,11 @@ static ModelData* openxr_newModelDataFB(XrHandTrackerEXT tracker, bool animated)
// Populate the data
result = xrGetHandMeshFB(tracker, &mesh);
if (XR_FAILED(result)) {
free(meshData);
lovrFree(meshData);
return NULL;
}
ModelData* model = calloc(1, sizeof(ModelData));
lovrAssert(model, "Out of memory");
ModelData* model = lovrCalloc(sizeof(ModelData));
model->ref = 1;
model->blobCount = 1;
model->bufferCount = 6;
@ -2134,8 +2124,7 @@ static ModelData* openxr_newModelDataFB(XrHandTrackerEXT tracker, bool animated)
model->nodeCount = 2 + jointCount;
lovrModelDataAllocate(model);
model->metadata = malloc(sizeof(XrHandTrackerEXT));
lovrAssert(model->metadata, "Out of memory");
model->metadata = lovrMalloc(sizeof(XrHandTrackerEXT));
*((XrHandTrackerEXT*)model->metadata) = tracker;
model->metadataSize = sizeof(XrHandTrackerEXT);
model->metadataType = META_HANDTRACKING_FB;
@ -2278,11 +2267,10 @@ static ModelData* openxr_newModelDataMSFT(XrControllerModelKeyMSFT modelKey, boo
return NULL;
}
unsigned char* modelData = malloc(size);
if (!modelData) return NULL;
unsigned char* modelData = lovrMalloc(size);
if (XR_FAILED(xrLoadControllerModelMSFT(state.session, modelKey, size, &size, modelData))) {
free(modelData);
lovrFree(modelData);
return NULL;
}
@ -2305,11 +2293,10 @@ static ModelData* openxr_newModelDataMSFT(XrControllerModelKeyMSFT modelKey, boo
return false;
}
free(model->metadata);
lovrFree(model->metadata);
model->metadataType = META_CONTROLLER_MSFT;
model->metadataSize = sizeof(MetadataControllerMSFT) + sizeof(uint32_t) * properties.nodeCountOutput;
model->metadata = malloc(model->metadataSize);
lovrAssert(model->metadata, "Out of memory");
model->metadata = lovrMalloc(model->metadataSize);
MetadataControllerMSFT* metadata = model->metadata;
metadata->modelKey = modelKey;
@ -2458,8 +2445,7 @@ static bool openxr_animate(Model* model) {
}
static Layer* openxr_newLayer(uint32_t width, uint32_t height) {
Layer* layer = calloc(1, sizeof(Layer));
lovrAssert(layer, "Out of memory");
Layer* layer = lovrCalloc(sizeof(Layer));
layer->ref = 1;
layer->width = width;
layer->height = height;
@ -2495,7 +2481,7 @@ static void openxr_destroyLayer(void* ref) {
Layer* layer = ref;
swapchain_destroy(&layer->swapchain);
lovrRelease(layer->pass, lovrPassDestroy);
free(layer);
lovrFree(layer);
}
static Layer** openxr_getLayers(uint32_t* count) {

View File

@ -13,6 +13,7 @@
#define MOVESPEED 3.f
#define SPRINTSPEED 15.f
#define SLOWSPEED .5f
#define MOVESMOOTH 30.f
#define TURNSPEED .005f
#define TURNSMOOTH 30.f
@ -67,7 +68,6 @@ static void onFocus(bool focused) {
static bool simulator_init(HeadsetConfig* config) {
state.config = *config;
state.epoch = os_get_time();
state.clipNear = .01f;
state.clipFar = 0.f;
state.distance = .5f;
@ -96,10 +96,13 @@ static void simulator_start(void) {
if (hasGraphics) {
state.pass = lovrPassCreate();
state.depthFormat = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F;
if (state.config.stencil && !lovrGraphicsGetFormatSupport(state.depthFormat, TEXTURE_FEATURE_RENDER)) {
state.depthFormat = FORMAT_D24S8; // Guaranteed to be supported if the other one isn't
if (!lovrGraphicsGetFormatSupport(state.depthFormat, TEXTURE_FEATURE_RENDER)) {
state.depthFormat = state.config.stencil ? FORMAT_D24S8 : FORMAT_D24;
}
}
state.epoch = os_get_time();
state.time = 0.;
}
static void simulator_stop(void) {
@ -135,6 +138,8 @@ static bool simulator_isSeated(void) {
static void simulator_getDisplayDimensions(uint32_t* width, uint32_t* height) {
float density = os_window_get_pixel_density();
os_window_get_size(width, height);
*width *= state.config.supersample;
*height *= state.config.supersample;
*width *= density;
*height *= density;
}
@ -269,7 +274,7 @@ static bool simulator_animate(struct Model* model) {
}
static Layer* simulator_newLayer(uint32_t width, uint32_t height) {
Layer* layer = calloc(1, sizeof(Layer));
Layer* layer = lovrCalloc(sizeof(Layer));
layer->ref = 1;
layer->textureWidth = width;
layer->textureWeight = height;
@ -278,7 +283,7 @@ static Layer* simulator_newLayer(uint32_t width, uint32_t height) {
static void simulator_destroyLayer(void* ref) {
Layer* layer = ref;
free(layer);
lovrFree(layer);
}
static Layer** simulator_getLayers(uint32_t* count) {
@ -379,7 +384,6 @@ static Pass* simulator_getPass(void) {
.height = height,
.layers = 1,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | TEXTURE_SAMPLE,
});
@ -437,8 +441,8 @@ static double simulator_update(void) {
state.pitch = CLAMP(state.pitch - (state.my - myprev) * TURNSPEED, -(float) M_PI / 2.f, (float) M_PI / 2.f);
state.yaw -= (state.mx - mxprev) * TURNSPEED;
} else {
state.mxHand = state.mx;
state.myHand = state.my;
state.mxHand = state.mx * state.config.supersample;
state.myHand = state.my * state.config.supersample;
}
// Head
@ -450,6 +454,7 @@ static double simulator_update(void) {
quat_slerp(state.headOrientation, target, 1.f - expf(-TURNSMOOTH * state.dt));
bool sprint = os_is_key_down(OS_KEY_LEFT_SHIFT) || os_is_key_down(OS_KEY_RIGHT_SHIFT);
bool slow = os_is_key_down(OS_KEY_LEFT_CONTROL) || os_is_key_down(OS_KEY_RIGHT_CONTROL);
bool front = os_is_key_down(OS_KEY_W) || os_is_key_down(OS_KEY_UP);
bool back = os_is_key_down(OS_KEY_S) || os_is_key_down(OS_KEY_DOWN);
bool left = os_is_key_down(OS_KEY_A) || os_is_key_down(OS_KEY_LEFT);
@ -461,7 +466,7 @@ static double simulator_update(void) {
velocity[0] = (left ? -1.f : right ? 1.f : 0.f);
velocity[1] = (down ? -1.f : up ? 1.f : 0.f);
velocity[2] = (front ? -1.f : back ? 1.f : 0.f);
vec3_scale(velocity, sprint ? SPRINTSPEED : MOVESPEED);
vec3_scale(velocity, sprint ? SPRINTSPEED : (slow ? SLOWSPEED : MOVESPEED));
vec3_lerp(state.velocity, velocity, 1.f - expf(-MOVESMOOTH * state.dt));
vec3_scale(vec3_init(velocity, state.velocity), state.dt);
@ -478,6 +483,8 @@ static double simulator_update(void) {
float ray[3];
uint32_t width, height;
os_window_get_size(&width, &height);
width *= state.config.supersample;
height *= state.config.supersample;
vec3_set(ray, state.mxHand / width * 2.f - 1.f, state.myHand / height * 2.f - 1.f, 1.f);
mat4_mulPoint(inverseProjection, ray);

View File

@ -129,10 +129,9 @@ static void evaluate(float* restrict P, size_t n, float t, vec4 p) {
}
Curve* lovrCurveCreate(void) {
Curve* curve = calloc(1, sizeof(Curve));
lovrAssert(curve, "Out of memory");
Curve* curve = lovrCalloc(sizeof(Curve));
curve->ref = 1;
arr_init(&curve->points, arr_alloc);
arr_init(&curve->points);
arr_reserve(&curve->points, 16);
return curve;
}
@ -140,12 +139,12 @@ Curve* lovrCurveCreate(void) {
void lovrCurveDestroy(void* ref) {
Curve* curve = ref;
arr_free(&curve->points);
free(curve);
lovrFree(curve);
}
void lovrCurveEvaluate(Curve* curve, float t, vec4 p) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to evaluate a Curve");
lovrAssert(t >= 0.f && t <= 1.f, "Curve evaluation interval must be within [0, 1]");
lovrCheck(curve->points.length >= 8, "Need at least 2 points to evaluate a Curve");
lovrCheck(t >= 0.f && t <= 1.f, "Curve evaluation interval must be within [0, 1]");
evaluate(curve->points.data, curve->points.length / 4, t, p);
}
@ -159,8 +158,8 @@ void lovrCurveGetTangent(Curve* curve, float t, vec4 p) {
}
Curve* lovrCurveSlice(Curve* curve, float t1, float t2) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to slice a Curve");
lovrAssert(t1 >= 0.f && t2 <= 1.f, "Curve slice interval must be within [0, 1]");
lovrCheck(curve->points.length >= 8, "Need at least 2 points to slice a Curve");
lovrCheck(t1 >= 0.f && t2 <= 1.f, "Curve slice interval must be within [0, 1]");
Curve* new = lovrCurveCreate();
arr_reserve(&new->points, curve->points.length);
@ -227,8 +226,7 @@ static const size_t vectorComponents[] = {
};
Pool* lovrPoolCreate(void) {
Pool* pool = calloc(1, sizeof(Pool));
lovrAssert(pool, "Out of memory");
Pool* pool = lovrCalloc(sizeof(Pool));
pool->ref = 1;
pool->data = os_vm_init((1 << 24) * sizeof(float));
lovrPoolGrow(pool, 1 << 12);
@ -238,7 +236,7 @@ Pool* lovrPoolCreate(void) {
void lovrPoolDestroy(void* ref) {
Pool* pool = ref;
os_vm_free(pool->data, (1 << 24) * sizeof(float));
free(pool);
lovrFree(pool);
}
void lovrPoolGrow(Pool* pool, size_t count) {
@ -271,7 +269,7 @@ Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data) {
}
float* lovrPoolResolve(Pool* pool, Vector vector) {
lovrAssert(vector.handle.generation == pool->generation, "Attempt to use a temporary vector from a previous frame");
lovrCheck(vector.handle.generation == pool->generation, "Attempt to use a temporary vector from a previous frame");
return pool->data + vector.handle.index;
}
@ -300,8 +298,7 @@ static uint64_t wangHash64(uint64_t key) {
// Use an 'Xorshift*' variant, as shown here: http://xorshift.di.unimi.it
RandomGenerator* lovrRandomGeneratorCreate(void) {
RandomGenerator* generator = calloc(1, sizeof(RandomGenerator));
lovrAssert(generator, "Out of memory");
RandomGenerator* generator = lovrCalloc(sizeof(RandomGenerator));
generator->ref = 1;
Seed seed = { .b32 = { .lo = 0xCBBF7A44, .hi = 0x0139408D } };
lovrRandomGeneratorSetSeed(generator, seed);
@ -310,7 +307,7 @@ RandomGenerator* lovrRandomGeneratorCreate(void) {
}
void lovrRandomGeneratorDestroy(void* ref) {
free(ref);
lovrFree(ref);
}
Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator) {

View File

@ -5,7 +5,7 @@
#pragma once
#define MAX_CONTACTS 10
#define MAX_TAGS 16
#define MAX_TAGS 32
#define NO_TAG ~0u
typedef struct World World;
@ -17,8 +17,10 @@ typedef Shape SphereShape;
typedef Shape BoxShape;
typedef Shape CapsuleShape;
typedef Shape CylinderShape;
typedef Shape ConvexShape;
typedef Shape MeshShape;
typedef Shape TerrainShape;
typedef Shape CompoundShape;
typedef Joint BallJoint;
typedef Joint DistanceJoint;
@ -26,8 +28,8 @@ typedef Joint HingeJoint;
typedef Joint SliderJoint;
typedef void (*CollisionResolver)(World* world, void* userdata);
typedef bool (*RaycastCallback)(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata);
typedef bool (*QueryCallback)(Shape* shape, void* userdata);
typedef bool (*RaycastCallback)(Collider* collider, float position[3], float normal[3], uint32_t child, void* userdata);
typedef bool (*QueryCallback)(Collider* collider, uint32_t child, void* userdata);
bool lovrPhysicsInit(void);
void lovrPhysicsDestroy(void);
@ -40,22 +42,40 @@ typedef struct {
float depth;
} Contact;
World* lovrWorldCreate(float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount);
typedef struct {
uint32_t maxColliders;
uint32_t maxColliderPairs;
uint32_t maxContacts;
bool allowSleep;
const char* tags[MAX_TAGS];
uint32_t tagCount;
} WorldInfo;
World* lovrWorldCreate(WorldInfo* info);
void lovrWorldDestroy(void* ref);
void lovrWorldDestroyData(World* world);
uint32_t lovrWorldGetColliderCount(World* world);
uint32_t lovrWorldGetJointCount(World* world);
Collider* lovrWorldGetColliders(World* world, Collider* collider);
Joint* lovrWorldGetJoints(World* world, Joint* joint);
void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata);
int lovrWorldGetStepCount(World* world);
void lovrWorldSetStepCount(World* world, int iterations);
void lovrWorldComputeOverlaps(World* world);
int lovrWorldGetNextOverlap(World* world, Shape** a, Shape** b);
int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float restitution);
void lovrWorldGetContacts(World* world, Shape* a, Shape* b, Contact contacts[MAX_CONTACTS], uint32_t* count);
void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata);
void lovrWorldRaycast(World* world, float start[3], float end[3], RaycastCallback callback, void* userdata);
bool lovrWorldQueryBox(World* world, float position[3], float size[3], QueryCallback callback, void* userdata);
bool lovrWorldQuerySphere(World* world, float position[3], float radius, QueryCallback callback, void* userdata);
Collider* lovrWorldGetFirstCollider(World* world);
void lovrWorldGetGravity(World* world, float* x, float* y, float* z);
void lovrWorldSetGravity(World* world, float x, float y, float z);
void lovrWorldGetGravity(World* world, float gravity[3]);
void lovrWorldSetGravity(World* world, float gravity[3]);
const char* lovrWorldGetTagName(World* world, uint32_t tag);
void lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2);
void lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2);
bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag);
// Deprecated
int lovrWorldGetStepCount(World* world);
void lovrWorldSetStepCount(World* world, int iterations);
float lovrWorldGetResponseTime(World* world);
void lovrWorldSetResponseTime(World* world, float responseTime);
float lovrWorldGetTightness(World* world);
@ -66,26 +86,22 @@ void lovrWorldGetAngularDamping(World* world, float* damping, float* threshold);
void lovrWorldSetAngularDamping(World* world, float damping, float threshold);
bool lovrWorldIsSleepingAllowed(World* world);
void lovrWorldSetSleepingAllowed(World* world, bool allowed);
const char* lovrWorldGetTagName(World* world, uint32_t tag);
void lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2);
void lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2);
bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag);
// Collider
Collider* lovrColliderCreate(World* world, float x, float y, float z);
Collider* lovrColliderCreate(World* world, Shape* shape, float position[3]);
void lovrColliderDestroy(void* ref);
void lovrColliderDestroyData(Collider* collider);
bool lovrColliderIsDestroyed(Collider* collider);
bool lovrColliderIsEnabled(Collider* collider);
void lovrColliderSetEnabled(Collider* collider, bool enable);
void lovrColliderInitInertia(Collider* collider, Shape* shape);
World* lovrColliderGetWorld(Collider* collider);
Collider* lovrColliderGetNext(Collider* collider);
void lovrColliderAddShape(Collider* collider, Shape* shape);
void lovrColliderRemoveShape(Collider* collider, Shape* shape);
Shape** lovrColliderGetShapes(Collider* collider, size_t* count);
Joint** lovrColliderGetJoints(Collider* collider, size_t* count);
void* lovrColliderGetUserData(Collider* collider);
void lovrColliderSetUserData(Collider* collider, void* data);
Joint* lovrColliderGetJoints(Collider* collider, Joint* joint);
Shape* lovrColliderGetShape(Collider* collider, uint32_t child);
void lovrColliderSetShape(Collider* collider, Shape* shape);
void lovrColliderGetShapeOffset(Collider* collider, float position[3], float orientation[4]);
void lovrColliderSetShapeOffset(Collider* collider, float position[3], float orientation[4]);
const char* lovrColliderGetTag(Collider* collider);
bool lovrColliderSetTag(Collider* collider, const char* tag);
float lovrColliderGetFriction(Collider* collider);
@ -94,38 +110,45 @@ float lovrColliderGetRestitution(Collider* collider);
void lovrColliderSetRestitution(Collider* collider, float restitution);
bool lovrColliderIsKinematic(Collider* collider);
void lovrColliderSetKinematic(Collider* collider, bool kinematic);
bool lovrColliderIsGravityIgnored(Collider* collider);
void lovrColliderSetGravityIgnored(Collider* collider, bool ignored);
bool lovrColliderIsSensor(Collider* collider);
void lovrColliderSetSensor(Collider* collider, bool sensor);
bool lovrColliderIsContinuous(Collider* collider);
void lovrColliderSetContinuous(Collider* collider, bool continuous);
float lovrColliderGetGravityScale(Collider* collider);
void lovrColliderSetGravityScale(Collider* collider, float scale);
bool lovrColliderIsSleepingAllowed(Collider* collider);
void lovrColliderSetSleepingAllowed(Collider* collider, bool allowed);
bool lovrColliderIsAwake(Collider* collider);
void lovrColliderSetAwake(Collider* collider, bool awake);
float lovrColliderGetMass(Collider* collider);
void lovrColliderSetMass(Collider* collider, float mass);
void lovrColliderGetMassData(Collider* collider, float* cx, float* cy, float* cz, float* mass, float inertia[6]);
void lovrColliderSetMassData(Collider* collider, float cx, float cy, float cz, float mass, float inertia[6]);
void lovrColliderGetPosition(Collider* collider, float* x, float* y, float* z);
void lovrColliderSetPosition(Collider* collider, float x, float y, float z);
void lovrColliderGetOrientation(Collider* collider, float* orientation);
void lovrColliderSetOrientation(Collider* collider, float* orientation);
void lovrColliderGetLinearVelocity(Collider* collider, float* x, float* y, float* z);
void lovrColliderSetLinearVelocity(Collider* collider, float x, float y, float z);
void lovrColliderGetAngularVelocity(Collider* collider, float* x, float* y, float* z);
void lovrColliderSetAngularVelocity(Collider* collider, float x, float y, float z);
void lovrColliderGetMassData(Collider* collider, float centerOfMass[3], float* mass, float inertia[6]);
void lovrColliderSetMassData(Collider* collider, float centerOfMass[3], float mass, float inertia[6]);
void lovrColliderGetPosition(Collider* collider, float position[3]);
void lovrColliderSetPosition(Collider* collider, float position[3]);
void lovrColliderGetOrientation(Collider* collider, float orientation[4]);
void lovrColliderSetOrientation(Collider* collider, float orientation[4]);
void lovrColliderGetLinearVelocity(Collider* collider, float velocity[3]);
void lovrColliderSetLinearVelocity(Collider* collider, float velocity[3]);
void lovrColliderGetAngularVelocity(Collider* collider, float velocity[3]);
void lovrColliderSetAngularVelocity(Collider* collider, float velocity[3]);
void lovrColliderGetLinearDamping(Collider* collider, float* damping, float* threshold);
void lovrColliderSetLinearDamping(Collider* collider, float damping, float threshold);
void lovrColliderGetAngularDamping(Collider* collider, float* damping, float* threshold);
void lovrColliderSetAngularDamping(Collider* collider, float damping, float threshold);
void lovrColliderApplyForce(Collider* collider, float x, float y, float z);
void lovrColliderApplyForceAtPosition(Collider* collider, float x, float y, float z, float cx, float cy, float cz);
void lovrColliderApplyTorque(Collider* collider, float x, float y, float z);
void lovrColliderGetLocalCenter(Collider* collider, float* x, float* y, float* z);
void lovrColliderGetLocalPoint(Collider* collider, float wx, float wy, float wz, float* x, float* y, float* z);
void lovrColliderGetWorldPoint(Collider* collider, float x, float y, float z, float* wx, float* wy, float* wz);
void lovrColliderGetLocalVector(Collider* collider, float wx, float wy, float wz, float* x, float* y, float* z);
void lovrColliderGetWorldVector(Collider* collider, float x, float y, float z, float* wx, float* wy, float* wz);
void lovrColliderGetLinearVelocityFromLocalPoint(Collider* collider, float x, float y, float z, float* vx, float* vy, float* vz);
void lovrColliderGetLinearVelocityFromWorldPoint(Collider* collider, float wx, float wy, float wz, float* vx, float* vy, float* vz);
void lovrColliderApplyForce(Collider* collider, float force[3]);
void lovrColliderApplyForceAtPosition(Collider* collider, float force[3], float position[3]);
void lovrColliderApplyTorque(Collider* collider, float torque[3]);
void lovrColliderApplyLinearImpulse(Collider* collider, float impulse[3]);
void lovrColliderApplyLinearImpulseAtPosition(Collider* collider, float impulse[3], float position[3]);
void lovrColliderApplyAngularImpulse(Collider* collider, float impulse[3]);
void lovrColliderGetLocalCenter(Collider* collider, float center[3]);
void lovrColliderGetLocalPoint(Collider* collider, float world[3], float local[3]);
void lovrColliderGetWorldPoint(Collider* collider, float local[3], float world[3]);
void lovrColliderGetLocalVector(Collider* collider, float world[3], float local[3]);
void lovrColliderGetWorldVector(Collider* collider, float local[3], float world[3]);
void lovrColliderGetLinearVelocityFromLocalPoint(Collider* collider, float point[3], float velocity[3]);
void lovrColliderGetLinearVelocityFromWorldPoint(Collider* collider, float point[3], float velocity[3]);
void lovrColliderGetAABB(Collider* collider, float aabb[6]);
// Shapes
@ -135,58 +158,57 @@ typedef enum {
SHAPE_BOX,
SHAPE_CAPSULE,
SHAPE_CYLINDER,
SHAPE_CONVEX,
SHAPE_MESH,
SHAPE_TERRAIN
SHAPE_TERRAIN,
SHAPE_COMPOUND
} ShapeType;
void lovrShapeDestroy(void* ref);
void lovrShapeDestroyData(Shape* shape);
ShapeType lovrShapeGetType(Shape* shape);
Collider* lovrShapeGetCollider(Shape* shape);
bool lovrShapeIsEnabled(Shape* shape);
void lovrShapeSetEnabled(Shape* shape, bool enabled);
bool lovrShapeIsSensor(Shape* shape);
void lovrShapeSetSensor(Shape* shape, bool sensor);
void* lovrShapeGetUserData(Shape* shape);
void lovrShapeSetUserData(Shape* shape, void* data);
void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z);
void lovrShapeSetPosition(Shape* shape, float x, float y, float z);
void lovrShapeGetOrientation(Shape* shape, float* orientation);
void lovrShapeSetOrientation(Shape* shape, float* orientation);
void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]);
void lovrShapeGetAABB(Shape* shape, float aabb[6]);
void lovrShapeGetMass(Shape* shape, float density, float centerOfMass[3], float* mass, float inertia[6]);
void lovrShapeGetAABB(Shape* shape, float position[3], float orientation[4], float aabb[6]);
SphereShape* lovrSphereShapeCreate(float radius);
float lovrSphereShapeGetRadius(SphereShape* sphere);
void lovrSphereShapeSetRadius(SphereShape* sphere, float radius);
BoxShape* lovrBoxShapeCreate(float w, float h, float d);
void lovrBoxShapeGetDimensions(BoxShape* box, float* w, float* h, float* d);
void lovrBoxShapeSetDimensions(BoxShape* box, float w, float h, float d);
BoxShape* lovrBoxShapeCreate(float dimensions[3]);
void lovrBoxShapeGetDimensions(BoxShape* box, float dimensions[3]);
CapsuleShape* lovrCapsuleShapeCreate(float radius, float length);
float lovrCapsuleShapeGetRadius(CapsuleShape* capsule);
void lovrCapsuleShapeSetRadius(CapsuleShape* capsule, float radius);
float lovrCapsuleShapeGetLength(CapsuleShape* capsule);
void lovrCapsuleShapeSetLength(CapsuleShape* capsule, float length);
CylinderShape* lovrCylinderShapeCreate(float radius, float length);
float lovrCylinderShapeGetRadius(CylinderShape* cylinder);
void lovrCylinderShapeSetRadius(CylinderShape* cylinder, float radius);
float lovrCylinderShapeGetLength(CylinderShape* cylinder);
void lovrCylinderShapeSetLength(CylinderShape* cylinder, float length);
ConvexShape* lovrConvexShapeCreate(float points[], uint32_t count);
MeshShape* lovrMeshShapeCreate(int vertexCount, float vertices[], int indexCount, uint32_t indices[]);
TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t widthSamples, uint32_t depthSamples, float horizontalScale, float verticalScale);
TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, float scaleY);
CompoundShape* lovrCompoundShapeCreate(Shape** shapes, float* positions, float* orientations, uint32_t count, bool freeze);
bool lovrCompoundShapeIsFrozen(CompoundShape* shape);
void lovrCompoundShapeAddChild(CompoundShape* shape, Shape* child, float position[3], float orientation[4]);
void lovrCompoundShapeReplaceChild(CompoundShape* shape, uint32_t index, Shape* child, float position[3], float orientation[4]);
void lovrCompoundShapeRemoveChild(CompoundShape* shape, uint32_t index);
Shape* lovrCompoundShapeGetChild(CompoundShape* shape, uint32_t index);
uint32_t lovrCompoundShapeGetChildCount(CompoundShape* shape);
void lovrCompoundShapeGetChildOffset(CompoundShape* shape, uint32_t index, float position[3], float orientation[4]);
void lovrCompoundShapeSetChildOffset(CompoundShape* shape, uint32_t index, float position[3], float orientation[4]);
// These tokens need to exist for Lua bindings
#define lovrSphereShapeDestroy lovrShapeDestroy
#define lovrBoxShapeDestroy lovrShapeDestroy
#define lovrCapsuleShapeDestroy lovrShapeDestroy
#define lovrCylinderShapeDestroy lovrShapeDestroy
#define lovrConvexShapeDestroy lovrShapeDestroy
#define lovrMeshShapeDestroy lovrShapeDestroy
#define lovrTerrainShapeDestroy lovrShapeDestroy
#define lovrCompoundShapeDestroy lovrShapeDestroy
// Joints
@ -199,14 +221,11 @@ typedef enum {
void lovrJointDestroy(void* ref);
void lovrJointDestroyData(Joint* joint);
bool lovrJointIsDestroyed(Joint* joint);
JointType lovrJointGetType(Joint* joint);
float lovrJointGetCFM(Joint* joint);
void lovrJointSetCFM(Joint* joint, float cfm);
float lovrJointGetERP(Joint* joint);
void lovrJointSetERP(Joint* joint, float erp);
void lovrJointGetColliders(Joint* joint, Collider** a, Collider** b);
void* lovrJointGetUserData(Joint* joint);
void lovrJointSetUserData(Joint* joint, void* data);
Collider* lovrJointGetColliderA(Joint* joint);
Collider* lovrJointGetColliderB(Joint* joint);
Joint* lovrJointGetNext(Joint* joint, Collider* collider);
bool lovrJointIsEnabled(Joint* joint);
void lovrJointSetEnabled(Joint* joint, bool enable);

File diff suppressed because it is too large Load Diff

View File

@ -188,3 +188,11 @@ bool lovrSystemWasMouseReleased(int button) {
float lovrSystemGetScrollDelta(void) {
return state.scrollDelta;
}
const char* lovrSystemGetClipboardText(void) {
return os_get_clipboard_text();
}
void lovrSystemSetClipboardText(const char* text) {
os_set_clipboard_text(text);
}

View File

@ -30,3 +30,5 @@ bool lovrSystemIsMouseDown(int button);
bool lovrSystemWasMousePressed(int button);
bool lovrSystemWasMouseReleased(int button);
float lovrSystemGetScrollDelta(void);
const char* lovrSystemGetClipboardText(void);
void lovrSystemSetClipboardText(const char* text);

View File

@ -1,6 +1,7 @@
#include "thread/thread.h"
#include "data/blob.h"
#include "event/event.h"
#include "core/job.h"
#include "core/os.h"
#include "util.h"
#include <math.h>
@ -38,10 +39,16 @@ static struct {
map_t channels;
} state;
bool lovrThreadModuleInit(void) {
bool lovrThreadModuleInit(int32_t workers) {
if (atomic_fetch_add(&state.ref, 1)) return false;
mtx_init(&state.channelLock, mtx_plain);
map_init(&state.channels, 0);
uint32_t cores = os_get_core_count();
if (workers < 0) workers += cores;
workers = MAX(workers, 0);
job_init(workers);
return true;
}
@ -54,6 +61,7 @@ void lovrThreadModuleDestroy(void) {
}
mtx_destroy(&state.channelLock);
map_free(&state.channels);
job_destroy();
memset(&state, 0, sizeof(state));
}
@ -102,8 +110,7 @@ static int threadFunction(void* data) {
}
Thread* lovrThreadCreate(ThreadFunction* function, Blob* body) {
Thread* thread = calloc(1, sizeof(Thread));
lovrAssert(thread, "Out of memory");
Thread* thread = lovrCalloc(sizeof(Thread));
thread->ref = 1;
thread->body = body;
thread->function = function;
@ -117,8 +124,8 @@ void lovrThreadDestroy(void* ref) {
mtx_destroy(&thread->lock);
if (thread->handle) thrd_detach(thread->handle);
lovrRelease(thread->body, lovrBlobDestroy);
free(thread->error);
free(thread);
lovrFree(thread->error);
lovrFree(thread);
}
void lovrThreadStart(Thread* thread, Variant* arguments, uint32_t argumentCount) {
@ -128,10 +135,10 @@ void lovrThreadStart(Thread* thread, Variant* arguments, uint32_t argumentCount)
return;
}
free(thread->error);
lovrFree(thread->error);
thread->error = NULL;
lovrAssert(argumentCount <= MAX_THREAD_ARGUMENTS, "Too many Thread arguments (max is %d)", MAX_THREAD_ARGUMENTS);
lovrCheck(argumentCount <= MAX_THREAD_ARGUMENTS, "Too many Thread arguments (max is %d)", MAX_THREAD_ARGUMENTS);
memcpy(thread->arguments, arguments, argumentCount * sizeof(Variant));
thread->argumentCount = argumentCount;
@ -162,10 +169,9 @@ const char* lovrThreadGetError(Thread* thread) {
// Channel
Channel* lovrChannelCreate(uint64_t hash) {
Channel* channel = calloc(1, sizeof(Channel));
lovrAssert(channel, "Out of memory");
Channel* channel = lovrCalloc(sizeof(Channel));
channel->ref = 1;
arr_init(&channel->messages, arr_alloc);
arr_init(&channel->messages);
mtx_init(&channel->lock, mtx_plain);
cnd_init(&channel->cond);
channel->hash = hash;
@ -178,7 +184,7 @@ void lovrChannelDestroy(void* ref) {
arr_free(&channel->messages);
mtx_destroy(&channel->lock);
cnd_destroy(&channel->cond);
free(channel);
lovrFree(channel);
}
bool lovrChannelPush(Channel* channel, Variant* variant, double timeout, uint64_t* id) {

View File

@ -14,7 +14,7 @@ struct Variant;
typedef struct Thread Thread;
typedef struct Channel Channel;
bool lovrThreadModuleInit(void);
bool lovrThreadModuleInit(int32_t workers);
void lovrThreadModuleDestroy(void);
struct Channel* lovrThreadGetChannel(const char* name);

View File

@ -1,42 +1,37 @@
#include "util.h"
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <setjmp.h>
#include <stdio.h>
// Error handling
static LOVR_THREAD_LOCAL errorFn* lovrErrorCallback;
static LOVR_THREAD_LOCAL void* lovrErrorUserdata;
// Allocation
void lovrSetErrorCallback(errorFn* callback, void* userdata) {
lovrErrorCallback = callback;
lovrErrorUserdata = userdata;
void* lovrMalloc(size_t size) {
void* data = malloc(size);
lovrAssert(data, "Out of memory");
return data;
}
void lovrThrow(const char* format, ...) {
va_list args;
va_start(args, format);
lovrErrorCallback(lovrErrorUserdata, format, args);
va_end(args);
exit(EXIT_FAILURE);
void* lovrCalloc(size_t size) {
void* data = calloc(1, size);
lovrAssert(data, "Out of memory");
return data;
}
// Logging
logFn* lovrLogCallback;
void* lovrLogUserdata;
void lovrSetLogCallback(logFn* callback, void* userdata) {
lovrLogCallback = callback;
lovrLogUserdata = userdata;
void* lovrRealloc(void* old, size_t size) {
void* data = realloc(old, size);
lovrAssert(data, "Out of memory");
return data;
}
void lovrLog(int level, const char* tag, const char* format, ...) {
va_list args;
va_start(args, format);
lovrLogCallback(lovrLogUserdata, level, tag, format, args);
va_end(args);
void lovrFree(void* data) {
free(data);
}
// Refcounting
#if ATOMIC_INT_LOCK_FREE != 2
#error "Lock-free integer atomics are not supported on this platform, but are required for refcounting"
#endif
@ -53,18 +48,119 @@ void lovrRelease(void* object, void (*destructor)(void*)) {
}
}
// Dynamic Array
// Default malloc-based allocator for arr_t (like realloc except well-defined when size is 0)
void* arr_alloc(void* data, size_t size) {
if (size > 0) {
return realloc(data, size);
} else {
free(data);
return NULL;
// Defer
typedef struct {
void (*fn)(void*);
void* arg;
} Closure;
static LOVR_THREAD_LOCAL struct {
Closure stack[16];
uint16_t releaseMask;
uint16_t errMask;
uint32_t top;
} defer;
uint32_t lovrDeferPush(void) {
return defer.top;
}
static void deferPop(uint32_t base, bool err) {
while (defer.top > base) {
uint32_t index = --defer.top;
Closure c = defer.stack[index];
if (err || (defer.errMask & (1u << index)) == 0) {
if (defer.releaseMask & (1u << index)) {
lovrRelease(c.arg, c.fn);
} else {
c.fn(c.arg);
}
}
}
}
void lovrDeferPop(uint32_t base) {
deferPop(base, false);
}
void lovrDefer(void (*fn)(void*), void* arg) {
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
defer.releaseMask &= ~(1u << defer.top);
defer.errMask &= ~(1u << defer.top);
defer.stack[defer.top++] = (Closure) { fn, arg };
}
void lovrErrDefer(void (*fn)(void*), void* arg) {
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
defer.releaseMask &= ~(1u << defer.top);
defer.errMask |= (1u << defer.top);
defer.stack[defer.top++] = (Closure) { fn, arg };
}
void lovrDeferRelease(void* object, void (*destructor)(void*)) {
if (!object) return;
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
defer.releaseMask |= (1u << defer.top);
defer.errMask &= ~(1u << defer.top);
defer.stack[defer.top++] = (Closure) { destructor, object };
}
// Exceptions
typedef struct Handler {
struct Handler* prev;
uint32_t baseDefer;
void (*catch)(void* arg, const char* format, va_list args);
void* arg;
jmp_buf env;
} Handler;
static LOVR_THREAD_LOCAL Handler* lovrHandler;
void lovrTry(void (*fn)(void*), void* arg, void(*catch)(void*, const char*, va_list), void* catchArg) {
lovrHandler = &(Handler) {
.prev = lovrHandler,
.baseDefer = defer.top,
.catch = catch,
.arg = arg
};
if (setjmp(lovrHandler->env) == 0) {
fn(arg);
}
lovrHandler = lovrHandler->prev;
}
void lovrThrow(const char* format, ...) {
deferPop(lovrHandler->baseDefer, true);
va_list args;
va_start(args, format);
lovrHandler->catch(lovrHandler->arg, format, args);
va_end(args);
longjmp(lovrHandler->env, 1);
}
// Logging
static fn_log* lovrLogCallback;
static void* lovrLogUserdata;
void lovrSetLogCallback(fn_log* callback, void* userdata) {
lovrLogCallback = callback;
lovrLogUserdata = userdata;
}
void lovrLog(int level, const char* tag, const char* format, ...) {
va_list args;
va_start(args, format);
lovrLogCallback(lovrLogUserdata, level, tag, format, args);
va_end(args);
}
// Hashmap
static void map_rehash(map_t* map) {
map_t old = *map;
map->size <<= 1;
@ -129,34 +225,9 @@ void map_set(map_t* map, uint64_t hash, uint64_t value) {
map->values[h] = value;
}
void map_remove(map_t* map, uint64_t hash) {
uint64_t h = map_find(map, hash);
if (map->hashes[h] == MAP_NIL) {
return;
}
uint64_t mask = map->size - 1;
uint64_t i = h;
do {
i = (i + 1) & mask;
uint64_t x = map->hashes[i] & mask;
// Removing a key from an open-addressed hash table is complicated
if ((i > h && (x <= h || x > i)) || (i < h && (x <= h && x > i))) {
map->hashes[h] = map->hashes[i];
map->values[h] = map->values[i];
h = i;
}
} while (map->hashes[i] != MAP_NIL);
map->hashes[i] = MAP_NIL;
map->values[i] = MAP_NIL;
map->used--;
}
// UTF-8
// https://github.com/starwing/luautf8
size_t utf8_decode(const char *s, const char *e, unsigned *pch) {
unsigned ch;

View File

@ -6,7 +6,7 @@
#define LOVR_VERSION_MAJOR 0
#define LOVR_VERSION_MINOR 17
#define LOVR_VERSION_PATCH 0
#define LOVR_VERSION_PATCH 1
#define LOVR_VERSION_ALIAS "Tritium Gourmet"
#ifdef _MSC_VER
@ -33,13 +33,28 @@
#define CHECK_SIZEOF(T) int(*_o)[sizeof(T)]=1
#define BREAK() __asm("int $3")
// Error handling
typedef void errorFn(void*, const char*, va_list);
void lovrSetErrorCallback(errorFn* callback, void* userdata);
LOVR_NORETURN void lovrThrow(const char* format, ...);
#define lovrAssert(c, ...) if (!(c)) { lovrThrow(__VA_ARGS__); }
#define lovrUnreachable() lovrThrow("Unreachable")
// Allocation
void* lovrMalloc(size_t size);
void* lovrCalloc(size_t size);
void* lovrRealloc(void* data, size_t size);
void lovrFree(void* data);
// Refcounting (to be refcounted, a struct must have a uint32_t refcount as its first field)
void lovrRetain(void* ref);
void lovrRelease(void* ref, void (*destructor)(void*));
// Defer
uint32_t lovrDeferPush(void);
void lovrDeferPop(uint32_t base);
void lovrDefer(void (*fn)(void*), void* arg);
void lovrErrDefer(void (*fn)(void*), void* arg);
void lovrDeferRelease(void* ref, void (*destructor)(void*));
// Exceptions
void lovrTry(void (*fn)(void*), void* arg, void (*catch)(void*, const char*, va_list), void* catchArg);
LOVR_NORETURN void lovrThrow(const char* format, ...);
#define lovrAssert(c, ...) do { if (!(c)) { lovrThrow(__VA_ARGS__); } } while(0)
#define lovrUnreachable() lovrThrow("Unreachable")
#ifdef LOVR_UNCHECKED
#define lovrCheck(c, ...) ((void) 0)
#else
@ -47,11 +62,42 @@ LOVR_NORETURN void lovrThrow(const char* format, ...);
#endif
// Logging
typedef void logFn(void*, int, const char*, const char*, va_list);
typedef void fn_log(void*, int, const char*, const char*, va_list);
enum { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR };
void lovrSetLogCallback(logFn* callback, void* userdata);
void lovrSetLogCallback(fn_log* callback, void* userdata);
void lovrLog(int level, const char* tag, const char* format, ...);
// Profiling
#ifdef LOVR_PROFILE
#include <TracyC.h>
#define lovrProfileMarkFrame() TracyCFrameMark
#define lovrProfileStart(id, label) TracyCZoneN(id, label, true)
#define lovrProfileEnd(id) TracyCZoneEnd(id)
#else
#define lovrProfileMarkFrame() ((void) 0)
#define lovrProfileStart(id, label) ((void) 0)
#define lovrProfileEnd(id) ((void) 0)
#endif
// Dynamic Array
#define arr_t(T) struct { T* data; size_t length, capacity; }
#define arr_init(a) (a)->data = NULL, (a)->length = 0, (a)->capacity = 0
#define arr_free(a) if ((a)->data) lovrFree((a)->data)
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data))
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++
#define arr_pop(a) (a)->data[--(a)->length]
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
#define arr_clear(a) (a)->length = 0
static inline void _arr_reserve(void** data, size_t n, size_t* capacity, size_t stride) {
if (*capacity >= n) return;
if (*capacity == 0) *capacity = 1;
while (*capacity < n) *capacity *= 2;
*data = lovrRealloc(*data, *capacity * stride);
}
// Hash function (FNV1a)
static inline uint64_t hash64(const void* data, size_t length) {
const uint8_t* bytes = (const uint8_t*) data;
@ -62,34 +108,7 @@ static inline uint64_t hash64(const void* data, size_t length) {
return hash;
}
// Refcounting
void lovrRetain(void* ref);
void lovrRelease(void* ref, void (*destructor)(void*));
// Dynamic Array
typedef void* arr_allocator(void* data, size_t size);
#define arr_t(T) struct { T* data; arr_allocator* alloc; size_t length, capacity; }
#define arr_init(a, allocator) (a)->data = NULL, (a)->length = 0, (a)->capacity = 0, (a)->alloc = allocator
#define arr_free(a) if ((a)->data) (a)->alloc((a)->data, 0)
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data), (a)->alloc)
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++
#define arr_pop(a) (a)->data[--(a)->length]
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
#define arr_clear(a) (a)->length = 0
void* arr_alloc(void* data, size_t size);
static inline void _arr_reserve(void** data, size_t n, size_t* capacity, size_t stride, arr_allocator* allocator) {
if (*capacity >= n) return;
if (*capacity == 0) *capacity = 1;
while (*capacity < n) *capacity *= 2;
*data = allocator(*data, *capacity * stride);
lovrAssert(*data, "Out of memory");
}
// Hashmap
// Hashmap (does not support removal)
typedef struct {
uint64_t* hashes;
uint64_t* values;
@ -103,7 +122,6 @@ void map_init(map_t* map, uint32_t n);
void map_free(map_t* map);
uint64_t map_get(map_t* map, uint64_t hash);
void map_set(map_t* map, uint64_t hash, uint64_t value);
void map_remove(map_t* map, uint64_t hash);
// UTF-8
size_t utf8_decode(const char *s, const char *e, unsigned *pch);