Previously, if you wanted to run compute operations that depend on the
results of prior compute operations, you had to put these in 2 different
passes, because logically all of the compute calls in a pass run "at the
same time" (or we're at least giving the GPU the freedom to do that).
Having to set up an entirely new pass just to synchronize 2 :compute
calls is pretty cumbersome, and incurs extra overhead. It would be
possible to change things so *every* :compute call waits for previous
computes to finish, but this would destroy GPU parallelism.
The Pass:barrier method lets compute calls within a pass synchronize
with each other, without requiring multiple passes. Adding a barrier
basically means "hey, wait for all the :compute calls before the barrier
to finish before running future :computes".
This lets things remain highly parallel but allows them to be easily
synchronized when needed.
Pass stores draw commands rather than sending them to Vulkan
immediately.
The main motivation is to allow more flexibility in the Lua API. Passes
are now regular objects, aren't invalidated whenever submit is called,
and can cache their draws across multiple frames. Draws can also be
internally culled, sorted, and batched.
Some API methods (tallies) are missing, and there are still some bugs to
fix, notably with background color.
- Add Buffer:newReadback
- Add Buffer:getData
- Buffer:getPointer works with permanent buffers
- Buffer:setData works with permanent buffers
- Buffer:clear works with permanent buffers
- Add Texture:newReadback
- Add Texture:getPixels
- Add Texture:setPixels
- Add Texture:clear
- Add Texture:generateMipmaps
- Buffer readbacks can now return tables in addition to Blobs using Readback:getData
Tally is coming back soon with an improved API, it's temporarily removed
since it made the transfer rework a bit easier.
Note that synchronous readbacks (Buffer:getData, Texture:getPixels)
internally call lovr.graphics.submit, so they invalidate existing Pass
objects. This will be improved soon.
Pass:plane was leaving its z scale uninitialized, which resulted in
a garbage normal matrix and other issues when using the transform in a
shader. Planes were the only shape affected by this.
When a single stencil action is provided in Pass:setStencilWrite, it now
maps to just passOp instead of all the ops. This matches expectations
and previous versions.
- Pass:mesh accepts tables for vertices/indices
- Add Pass:setVertexFormat to set format used for table-based meshes
- Pass:send accepts tables for buffers
- Pass:send supports arbitrarily nested structs/arrays for push constants
- Buffer formats support arbitrarily nested structs/arrays
- Zero-length buffers are valid and represent structs
- Fields can have names using 'name'
- Field types can be tables of other fields (structs)
- Fields can have 'length' key
- newBuffer syntax has been changed to put format first (old version
still works)
- Buffers can be created from shader variables, avoiding need to declare
matching format.
- Pass:clear/Pass:read use byte offsets instead of indices
- Pass:copy uses byte offsets when copying a Buffer to a Buffer
- Deprecate lovr.graphics.getBuffer (tables can be used instead)
Currently if you create a texture from dimensions (assumed to be a
render target), its mipmap count differs depending on whether you
provide an options table. Now it will consistently be 1.
If the target index is missing, the state will apply to all targets.
Fixes undefined behavior when setting color state in a pass with
multiple color attachments.
They are copied by value. Upon popping, they are pushed as temporary
vectors. Matrices are allocated on the heap, everything else is stored
in the Variant itself.
This enables a few things:
- Custom vector methods can be added/replaced.
- Vector methods can be called as non-methods e.g. vec3.normalize(v)
- Possibility for vector constants in an __index metamethod.
This was measured as increasing overhead by 3.5% when creating temporary
vectors (2ns), which is arbitrarily deemed acceptable.
- Allow multisampled render pass to have a single-sample depth attachment
- Add a new depthResolve feature, indicating whether it's supported
- Fix stencil load/save
- Minor changes to render pass caching
- Currently the depth resolve is done using the first sample. A future
improvement would be to expose/use the min/max/average resolve modes.
- Put channel into thread module file.
- Make thread internals private.
- Handle more thread bookkeeping in thread module instead of Lua API.
- Fix a few race conditions/leaks nobody was probably ever going to hit.
Although the name is unfortunate, this allows access to lovr.headset
when no window is opened or when the graphics module is disabled. This
requires the XR_MND_headless extension to be supported by the runtime.
- Add helper functions for creating shapes to avoid duplication between
newShape and newShapeCollider.
- Add lovr.physics.newMeshShape and lovr.physics.newTerrainShape
- Register TerrainShape so it has all the base Shape methods
- Smooth out a few TerrainShape warnings
A null-char is valid part of Lua string. When such a string is sent
through the channel, its length should be stored as well to be able to
correctly reconstruct it on the other thread.
The bug was triggered with this code:
s1 = 'a \0 b'
print(#s1) -- 5
ch:push(s1)
s2 = ch:pop()
print(#s2) -- 2
- glowTexture is on by default, but still requires the glow flag.
- occlusionTexture is named ambientOcclusion, and is on by default,
but is still not used by any builtin shaders/helpers.
Sigh, back to getPass. I don't even know at this point. Basically now
that we came up with a half-solution for temp buffers, it makes sense to
apply this to passes as well, since we aren't going with the workstream
idea and temp passes are more convenient than retained passes.
- They no longer live in temporary memory, but in a dedicated pool.
- There are error checks for using a temporary buffer after it's invalid
- However, these are imperfect, and could be improved. One idea is to
avoid recycling a temporary buffer until its refcount decays (i.e.
Lua finally decides to garbage collect it). This would explode
memory usage sometimes, so it could only be enabled when
t.graphics.debug is true.
A lot of clean up can happen now that C doesn't push delayed errors to
Lua. This was happening for Pico and WebVR, neither of which are used
anymore.
Also default vsync to true but force it off if VR is active.
They are now destroyed explicitly after tearing down the Lua state
instead of relying on finalizers. It's definitely annoying to make it
coordinated in a centralized way like this instead of being distributed,
but there's not really any reliabel way to ensure that graphics objects
are destroyed before the graphics module/device is destroyed, which is a
problem.
It uses newPass instead of getPass. Temporary objects had lifetime
issues that were nearly impossible to solve. And normal objects are
easier to understand because they behave like all other LÖVR objects.
However, Pass commands are not retained from frame to frame. Pass
objects must be re-recorded before every submit, and must be reset
before being recorded again.
Pass objects now provide a natural place for render-pass-related info
like clears and texture handles. They also allow more information to be
precomputed which should reduce overhead a bit.
It is now possible to request a stencil buffer and antialiasing on the
window and headset textures, via conf.lua.
lovr.graphics.setBackground should instead set the clear color on the
window pass. Though we're still going to try to do spherical harmonics
in some capacity.
There are still major issues with OpenXR that are going to be ironed
out, and the desktop driver hasn't been converted over to the new
headset Pass system yet. So lovr.headset integration is a bit WIP.
- Rename/reorder some projection matrix functions.
- Make perspective functions flip Y and use 0-1 NDC range.
- Flip winding and font vertices based on handedness.
This stuff is really confusing
I guess it's better to match them up with the default attributes.
In most cases you're going to want to manually specify them with names
or numbers anyway.