Browse Source

Some Buffer docs;

bjorn 1 year ago
parent
commit
24de2b52f0

File diff suppressed because it is too large
+ 328 - 120
api/init.lua


+ 5 - 1
api/lovr/graphics/Buffer/getSize.lua

@@ -1,6 +1,10 @@
 return {
 return {
   summary = 'Get the size of the Buffer, in bytes.',
   summary = 'Get the size of the Buffer, in bytes.',
-  description = 'Returns the size of the Buffer, in bytes.  This is the same as `length * stride`.',
+  description = [[
+    Returns the size of the Buffer in VRAM, in bytes.  This is the same as `length * stride`.
+
+    The size of the Buffer can't change after it's created.
+  ]],
   arguments = {},
   arguments = {},
   returns = {
   returns = {
     size = {
     size = {

+ 43 - 31
api/lovr/graphics/Buffer/init.lua

@@ -1,39 +1,51 @@
 return {
 return {
   summary = 'A block of memory on the GPU.',
   summary = 'A block of memory on the GPU.',
   description = [[
   description = [[
-    A Buffer is a block of GPU memory.  Buffers are similar to Lua tables or arrays: they have a
-    length and store a list of values.  The length of a Buffer and its format (the type of each
-    value) are declared upfront and can't be changed.  Each value of a Buffer consists of one or
-    more fields, and each field has a type.  For example, if a Buffer is used to store vertices,
-    each value might store 3 fields for the position, normal vector, and UV coordinates of a vertex.
-
-    Buffers are commonly used for:
-
-    - Mesh data: Buffers hold the data that define the vertices in a mesh. Buffers also store the
-      vertex indices of a mesh, which define the order the vertices are connected together into
-      triangles. These are often called vertex buffers and index buffers.
-    - Shader data: Buffers can be bound to a Shader, letting the Shader read arbitrary data. For
-      example, Lua code could create a Buffer with the positions and colors of all the lights in a
-      scene, which a Shader can use to do lighting calculations.
-    - Compute: Compute shaders can write data to Buffers.  This GPU-generated data can be used in
-      later rendering work or sent back to Lua.
-    - Indirect: Indirect rendering is an advanced technique where instructions for rendering work
-      are recorded to a Buffer (potentially by a compute shader) and later drawn.
-
-    There are two types of Buffers:
-
-    - **Temporary** buffers are very inexpensive to create, and writing to them from Lua is fast.
-      However, they become invalid at the end of `lovr.draw` (i.e. when `lovr.graphics.submit` is
-      called).  The GPU is slightly slower at accessing data from temporary buffers, and compute
-      shaders can not write to them.  They are designed for storing data that changes every frame.
-    - **Permanent** buffers are more expensive to create, and updating their contents requires a
-      transfer from CPU memory to VRAM.  They act like normal LÖVR objects and don't need to be
-      recreated every frame.  They often have faster performance when used by the GPU, and compute
-      shaders can write to them.  They are great for large pieces of data that are initialized once
-      at load time, or data that is updated infrequently.
+    A Buffer is a block of memory on the GPU.  It's like a GPU version of a `Blob`.  Lua code can
+    write data to the buffer which uploads to VRAM, and shaders read buffer data during rendering.
+    Compute shaders can also write to buffers.
+
+    The **size** of a Buffer is the number of bytes of VRAM it occupies.  It's set when the Buffer
+    is created and can't be changed afterwards.
+
+    Buffers can optionally have a **format**, which defines the type of data stored in the buffer.
+    The format determines how Lua values are converted into binary.  Like the size, it can't change
+    after the buffer is created.  `Shader:getBufferFormat` returns the format of a variable in a
+    `Shader`.
+
+    When a Buffer has a format, it also has a **length**, which is the number of items it holds, and
+    a **stride**, which is the number of bytes between each item.
+
+    `Buffer:setData` is used to upload data to the Buffer.  `Buffer:clear` can also be used to
+    efficiently zero out a Buffer.
+
+    `Buffer:getData` can be used to download data from the Buffer, but be aware that it stalls the
+    GPU until the download is complete, which is very slow!  `Buffer:newReadback` will instead
+    download the data in the background, which avoids costly stalls.
+
+    Buffers are often used for mesh data.  Vertices stored in buffers can be drawn using
+    `Pass:mesh`.  `Mesh` objects can also be used, which wrap Buffers along with some extra
+    metadata.
+
+    Buffers can be "bound" to a variable in a Shader using `Pass:send`.  That means that the next
+    time the shader runs, the data from the Buffer will be used for the stuff in the variable.
+
+    It's important to understand that data from a Buffer will only be used at the point when
+    graphics commands are actually submitted.  This example records 2 draws, changing the buffer
+    data between each one:
+
+        buffer:setData(data1)
+        pass:mesh(buffer)
+        buffer:setData(data2)
+        pass:mesh(buffer)
+        lovr.graphics.submit(pass)
+
+    **Both** draws will use `data2` here!  That's because `lovr.graphics.submit` is where the draws
+    actually get processed, so they both see the "final" state of the buffer.  The data in a Buffer
+    can't be 2 things at once!  If you need multiple versions of data, it's best to use a bigger
+    buffer with offsets (or multiple buffers).
   ]],
   ]],
   constructors = {
   constructors = {
-    'lovr.graphics.getBuffer',
     'lovr.graphics.newBuffer'
     'lovr.graphics.newBuffer'
   }
   }
 }
 }

+ 1 - 2
api/lovr/graphics/BufferLayout.lua → api/lovr/graphics/DataLayout.lua

@@ -32,9 +32,8 @@ return {
   },
   },
   related = {
   related = {
     'lovr.graphics.newBuffer',
     'lovr.graphics.newBuffer',
-    'lovr.graphics.getBuffer',
     'Buffer:getFormat',
     'Buffer:getFormat',
     'Buffer:getStride',
     'Buffer:getStride',
-    'FieldType'
+    'DataType'
   }
   }
 }
 }

+ 0 - 0
api/lovr/graphics/FieldType.lua → api/lovr/graphics/DataType.lua


+ 4 - 3
api/lovr/graphics/getBuffer.lua

@@ -1,4 +1,5 @@
 return {
 return {
+  deprecated = true,
   tag = 'graphics-objects',
   tag = 'graphics-objects',
   summary = 'Get a temporary Buffer.',
   summary = 'Get a temporary Buffer.',
   description = 'Creates a temporary Buffer.',
   description = 'Creates a temporary Buffer.',
@@ -23,7 +24,7 @@ return {
       ]]
       ]]
     },
     },
     type = {
     type = {
-      type = 'FieldType',
+      type = 'DataType',
       description = 'The type of each item in the Buffer.'
       description = 'The type of each item in the Buffer.'
     },
     },
     format = {
     format = {
@@ -85,10 +86,10 @@ return {
     }
     }
   },
   },
   notes = [[
   notes = [[
-    The format table can contain a list of `FieldType`s or a list of tables to provide extra
+    The format table can contain a list of `DataType`s or a list of tables to provide extra
     information about each field.  Each inner table has the following keys:
     information about each field.  Each inner table has the following keys:
 
 
-    - `type` is the `FieldType` of the field and is required.
+    - `type` is the `DataType` of the field and is required.
     - `offset` is the byte offset of the field.  Any fields with a `nil` offset will be placed next
     - `offset` is the byte offset of the field.  Any fields with a `nil` offset will be placed next
       to each other sequentially in memory, subject to any padding required by the Buffer's layout.
       to each other sequentially in memory, subject to any padding required by the Buffer's layout.
       In practice this means that an `offset` should be set for either all of the fields or none of
       In practice this means that an `offset` should be set for either all of the fields or none of

+ 100 - 17
api/lovr/graphics/newBuffer.lua

@@ -3,8 +3,13 @@ return {
   summary = 'Create a new Buffer.',
   summary = 'Create a new Buffer.',
   description = 'Creates a Buffer.',
   description = 'Creates a Buffer.',
   arguments = {
   arguments = {
+    size = {
+      type = 'number',
+      description = 'The size of the Buffer, in bytes.'
+    },
     length = {
     length = {
       type = 'number',
       type = 'number',
+      default = '1',
       description = 'The length of the Buffer.'
       description = 'The length of the Buffer.'
     },
     },
     data = {
     data = {
@@ -23,20 +28,16 @@ return {
       ]]
       ]]
     },
     },
     type = {
     type = {
-      type = 'FieldType',
+      type = 'DataType',
       description = 'The type of each item in the Buffer.'
       description = 'The type of each item in the Buffer.'
     },
     },
     format = {
     format = {
       type = 'table',
       type = 'table',
-      default = 'nil',
-      description = [[
-        A list of fields in the Buffer (see notes).  `nil` is a valid format, but means only `Blob`s
-        can be written to the Buffer from Lua.
-      ]],
+      description = 'A list of fields in the Buffer.',
       table = {
       table = {
         {
         {
           name = 'layout',
           name = 'layout',
-          type = 'BufferLayout',
+          type = 'DataLayout',
           default = 'packed',
           default = 'packed',
           description = 'How to lay out the Buffer fields in memory.'
           description = 'How to lay out the Buffer fields in memory.'
         },
         },
@@ -60,50 +61,132 @@ return {
   },
   },
   variants = {
   variants = {
     {
     {
+      arguments = { 'size' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'blob' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'format', 'length' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'format', 'data' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'format', 'blob' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'type', 'length' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'type', 'data' },
+      returns = { 'buffer' }
+    },
+    {
+      arguments = { 'type', 'blob' },
+      returns = { 'buffer' }
+    },
+    {
+      deprecated = true,
       arguments = { 'length', 'type' },
       arguments = { 'length', 'type' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     },
     },
     {
     {
+      deprecated = true,
       arguments = { 'data', 'type' },
       arguments = { 'data', 'type' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     },
     },
     {
     {
+      deprecated = true,
       arguments = { 'length', 'format' },
       arguments = { 'length', 'format' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     },
     },
     {
     {
+      deprecated = true,
       arguments = { 'data', 'format' },
       arguments = { 'data', 'format' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     },
     },
     {
     {
+      deprecated = true,
       arguments = { 'blob', 'type' },
       arguments = { 'blob', 'type' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     },
     },
     {
     {
+      deprecated = true,
       arguments = { 'blob', 'format' },
       arguments = { 'blob', 'format' },
       returns = { 'buffer' }
       returns = { 'buffer' }
     }
     }
   },
   },
   notes = [[
   notes = [[
-    The format table can contain a list of `FieldType`s or a list of tables to provide extra
+    The format table can contain a list of `DataType`s or a list of tables to provide extra
     information about each field.  Each inner table has the following keys:
     information about each field.  Each inner table has the following keys:
 
 
-    - `type` is the `FieldType` of the field and is required.
+    - `type` is the `DataType` of the field and is required.
+    - `name` is the name of the field, used to match table keys and vertex attribute names.
     - `offset` is the byte offset of the field.  Any fields with a `nil` offset will be placed next
     - `offset` is the byte offset of the field.  Any fields with a `nil` offset will be placed next
       to each other sequentially in memory, subject to any padding required by the Buffer's layout.
       to each other sequentially in memory, subject to any padding required by the Buffer's layout.
       In practice this means that you probably want to provide an `offset` for either all of the
       In practice this means that you probably want to provide an `offset` for either all of the
       fields or none of them.
       fields or none of them.
-    - `location` is the vertex attribute location of each field.  This is used to match up each
-      field with an attribute declared in a shader, and doesn't have any purpose when binding the
-      buffer as a uniform or storage buffer.  Any fields with a `nil` location will use an
-      autoincrementing location starting at zero.  Named locations are not currently supported, but
-      may be added in the future.
+    - `length` is the array size of the field.
 
 
-    If no table or Blob is used to define the initial Buffer contents, its data will be undefined.
+    As a shorthand, the name, type, and optionally the length of a field can be provided as a list
+    instead of using keys.
 
 
-    There is currently a max of 16 fields.
+    If no table or Blob is used to define the initial Buffer contents, its data will be undefined.
   ]],
   ]],
+  examples = {
+    description = 'Examples of different buffer formats.',
+    code = [[
+      -- 2 matrices
+      lovr.graphics.newBuffer('mat4', 2)
+
+      -- 3 integers, with initial data
+      lovr.graphics.newBuffer('int', { 1, 2, 3 })
+
+      -- a simple mesh:
+      lovr.graphics.newBuffer({
+        { name = 'VertexPosition', type = 'vec3' },
+        { name = 'VertexColor', type = 'color' }
+      }, 4)
+
+      -- a uniform buffer with vec3's, using the std140 packing
+      lovr.graphics.newBuffer({ 'vec3', layout = 'std140' }, data)
+
+      -- a uniform buffer with key-value fields
+      lovr.graphics.newBuffer({
+        { 'AmbientColor', 'vec3' },
+        { 'LightPosition', 'vec3' },
+        { 'LightType', 'u32' },
+        { 'LightColor', 'vec4' },
+        layout = 'std140'
+      })
+
+      -- a buffer with nested structure and array types
+      lovr.graphics.newBuffer({
+        { 'globals', {
+          { 'ObjectCount', 'int' },
+          { 'WorldSize', 'vec2' },
+          { 'Scale', 'float' }
+        }},
+        { 'materials', {
+          { 'Color', 'vec4' },
+          { 'Glow', 'vec3' },
+          { 'Roughness', 'float' }
+        }, length = 32 },
+        layout = 'std430'
+      })
+
+      -- a buffer using a variable from a shader:
+      lovr.graphics.newBuffer(shader:getBufferFormat('transforms'))
+    ]]
+  },
   related = {
   related = {
-    'lovr.graphics.getBuffer'
+    'Shader:getBufferFormat'
   }
   }
 }
 }

Some files were not shown because too many files changed in this diff