2016-11-19 09:28:01 +00:00
# include "graphics/graphics.h"
2022-05-09 18:47:06 +00:00
# include "data/blob.h"
2022-04-30 03:38:34 +00:00
# include "data/image.h"
2022-07-04 00:26:31 +00:00
# include "data/modelData.h"
2022-06-19 00:43:12 +00:00
# include "data/rasterizer.h"
2022-06-06 03:38:14 +00:00
# include "headset/headset.h"
2022-05-11 19:51:13 +00:00
# include "math/math.h"
2022-04-21 07:27:13 +00:00
# include "core/gpu.h"
2022-05-07 00:26:38 +00:00
# include "core/maf.h"
2022-05-22 22:09:09 +00:00
# include "core/spv.h"
2022-04-27 05:51:24 +00:00
# include "core/os.h"
2022-04-21 07:27:13 +00:00
# include "util.h"
2022-06-22 03:05:57 +00:00
# include "monkey.h"
2022-05-28 03:47:07 +00:00
# include "shaders.h"
2022-04-30 03:38:34 +00:00
# include <math.h>
2022-08-09 03:36:22 +00:00
# include <limits.h>
2022-04-26 22:32:54 +00:00
# include <stdlib.h>
2022-04-20 07:38:21 +00:00
# include <string.h>
2022-05-09 18:47:06 +00:00
# ifdef LOVR_USE_GLSLANG
# include "glslang_c_interface.h"
# include "resource_limits_c.h"
# endif
2019-06-28 05:17:50 +00:00
2022-05-11 22:28:04 +00:00
uint32_t os_vk_create_surface ( void * instance , void * * surface ) ;
const char * * os_vk_get_instance_extensions ( uint32_t * count ) ;
2022-04-27 05:44:44 +00:00
# define MAX_FRAME_MEMORY (1 << 30)
2022-06-04 21:28:23 +00:00
# define MAX_SHADER_RESOURCES 32
2022-07-30 22:11:46 +00:00
# define MATERIALS_PER_BLOCK 256
2022-07-12 03:52:35 +00:00
# define FLOAT_BITS(f) ((union { float f; uint32_t u; }) { f }).u
2022-04-27 05:44:44 +00:00
2022-06-19 00:43:12 +00:00
typedef struct {
struct { float x , y , z ; } position ;
struct { float x , y , z ; } normal ;
struct { float u , v ; } uv ;
} ShapeVertex ;
2022-07-04 00:26:31 +00:00
typedef struct {
struct { float x , y , z ; } position ;
struct { float x , y , z ; } normal ;
struct { float u , v ; } uv ;
struct { uint8_t r , g , b , a ; } color ;
struct { float x , y , z ; } tangent ;
} ModelVertex ;
2022-06-04 21:28:23 +00:00
typedef struct {
gpu_phase readPhase ;
gpu_phase writePhase ;
gpu_cache pendingReads ;
2022-06-12 05:55:43 +00:00
gpu_cache pendingWrite ;
2022-06-04 21:28:23 +00:00
uint32_t lastWriteIndex ;
} Sync ;
2022-04-26 22:32:54 +00:00
struct Buffer {
uint32_t ref ;
uint32_t size ;
2022-05-30 19:18:42 +00:00
char * pointer ;
2022-04-26 22:32:54 +00:00
gpu_buffer * gpu ;
BufferInfo info ;
2022-06-16 03:46:43 +00:00
uint64_t hash ;
2022-06-04 21:28:23 +00:00
Sync sync ;
2022-04-26 22:32:54 +00:00
} ;
2022-04-30 03:38:34 +00:00
struct Texture {
uint32_t ref ;
2022-08-06 01:36:51 +00:00
uint32_t xrTick ;
2022-04-30 03:38:34 +00:00
gpu_texture * gpu ;
gpu_texture * renderView ;
2022-06-25 02:38:45 +00:00
Material * material ;
2022-04-30 03:38:34 +00:00
TextureInfo info ;
2022-06-04 21:28:23 +00:00
Sync sync ;
2022-04-30 03:38:34 +00:00
} ;
2022-05-01 22:47:17 +00:00
struct Sampler {
uint32_t ref ;
gpu_sampler * gpu ;
SamplerInfo info ;
} ;
2022-05-22 22:09:09 +00:00
typedef struct {
uint32_t hash ;
uint32_t offset ;
FieldType type ;
} ShaderConstant ;
typedef struct {
uint32_t hash ;
uint32_t binding ;
uint32_t stageMask ;
gpu_slot_type type ;
} ShaderResource ;
2022-06-16 03:46:43 +00:00
typedef struct {
uint32_t location ;
uint32_t hash ;
} ShaderAttribute ;
2022-05-09 18:47:06 +00:00
struct Shader {
uint32_t ref ;
2022-05-22 22:10:07 +00:00
Shader * parent ;
2022-05-09 18:47:06 +00:00
gpu_shader * gpu ;
ShaderInfo info ;
2022-08-07 01:05:30 +00:00
size_t layout ;
size_t computePipelineIndex ;
2022-08-06 20:06:42 +00:00
uint32_t workgroupSize [ 3 ] ;
2022-05-24 05:32:36 +00:00
uint32_t bufferMask ;
uint32_t textureMask ;
uint32_t samplerMask ;
uint32_t storageMask ;
2022-05-22 22:09:09 +00:00
uint32_t constantSize ;
uint32_t constantCount ;
uint32_t resourceCount ;
2022-06-16 03:46:43 +00:00
uint32_t attributeCount ;
2022-05-22 22:09:09 +00:00
ShaderConstant * constants ;
ShaderResource * resources ;
2022-06-16 03:46:43 +00:00
ShaderAttribute * attributes ;
2022-05-22 22:09:09 +00:00
uint32_t flagCount ;
uint32_t overrideCount ;
gpu_shader_flag * flags ;
uint32_t * flagLookup ;
2022-06-16 03:46:43 +00:00
bool hasCustomAttributes ;
2022-05-09 18:47:06 +00:00
} ;
2022-06-17 06:49:09 +00:00
struct Material {
uint32_t ref ;
uint32_t next ;
uint32_t tick ;
uint16_t index ;
uint16_t block ;
gpu_bundle * bundle ;
MaterialInfo info ;
2022-08-06 07:50:25 +00:00
bool hasWritableTexture ;
2022-06-17 06:49:09 +00:00
} ;
2022-06-19 00:43:12 +00:00
typedef struct {
uint32_t codepoint ;
float advance ;
2022-06-27 03:28:30 +00:00
uint16_t x , y ;
uint16_t uv [ 4 ] ;
2022-06-21 01:26:15 +00:00
float box [ 4 ] ;
2022-06-19 00:43:12 +00:00
} Glyph ;
struct Font {
uint32_t ref ;
FontInfo info ;
Material * material ;
arr_t ( Glyph ) glyphs ;
map_t glyphLookup ;
2022-06-21 01:26:15 +00:00
map_t kerning ;
2022-06-19 00:43:12 +00:00
float pixelDensity ;
2022-06-26 02:54:13 +00:00
float lineSpacing ;
2022-06-27 03:28:30 +00:00
uint32_t padding ;
2022-06-19 00:43:12 +00:00
Texture * atlas ;
uint32_t atlasWidth ;
uint32_t atlasHeight ;
uint32_t rowHeight ;
uint32_t atlasX ;
uint32_t atlasY ;
} ;
2022-05-30 22:36:31 +00:00
typedef struct {
float transform [ 16 ] ;
float cofactor [ 16 ] ;
float color [ 4 ] ;
} DrawData ;
2022-06-12 02:07:46 +00:00
typedef enum {
VERTEX_SHAPE ,
VERTEX_POINT ,
VERTEX_GLYPH ,
2022-07-04 00:26:31 +00:00
VERTEX_MODEL ,
2022-06-25 02:59:48 +00:00
VERTEX_EMPTY ,
2022-07-15 02:23:02 +00:00
VERTEX_FORMAX
2022-06-17 06:49:09 +00:00
} VertexFormat ;
2022-06-12 02:07:46 +00:00
typedef struct {
2022-07-12 03:52:35 +00:00
uint64_t hash ;
2022-07-13 02:35:23 +00:00
MeshMode mode ;
2022-06-12 02:07:46 +00:00
DefaultShader shader ;
2022-06-17 06:49:09 +00:00
Material * material ;
2022-06-12 02:07:46 +00:00
float * transform ;
struct {
Buffer * buffer ;
2022-06-17 06:49:09 +00:00
VertexFormat format ;
2022-06-12 02:07:46 +00:00
uint32_t count ;
2022-07-04 02:59:51 +00:00
void * * pointer ;
2022-06-12 02:07:46 +00:00
} vertex ;
struct {
Buffer * buffer ;
uint32_t count ;
2022-07-04 02:59:51 +00:00
void * * pointer ;
2022-06-12 02:07:46 +00:00
} index ;
uint32_t start ;
uint32_t count ;
uint32_t instances ;
uint32_t base ;
} Draw ;
2022-07-04 00:26:31 +00:00
typedef struct {
float properties [ 3 ] [ 4 ] ;
} NodeTransform ;
struct Model {
uint32_t ref ;
ModelInfo info ;
Draw * draws ;
Buffer * rawVertexBuffer ;
Buffer * vertexBuffer ;
Buffer * indexBuffer ;
Buffer * skinBuffer ;
Texture * * textures ;
Material * * materials ;
NodeTransform * localTransforms ;
float * globalTransforms ;
bool transformsDirty ;
uint32_t lastReskin ;
} ;
2022-07-14 07:05:58 +00:00
struct Readback {
uint32_t ref ;
uint32_t tick ;
uint32_t size ;
Readback * next ;
ReadbackInfo info ;
gpu_buffer * buffer ;
void * pointer ;
Image * image ;
2022-07-17 16:50:15 +00:00
Blob * blob ;
2022-07-14 07:05:58 +00:00
void * data ;
} ;
2022-07-04 00:26:31 +00:00
struct Tally {
uint32_t ref ;
uint32_t tick ;
TallyInfo info ;
gpu_tally * gpu ;
gpu_buffer * buffer ;
} ;
2022-08-06 20:37:27 +00:00
typedef struct {
float resolution [ 4 ] ;
float time ;
} Globals ;
2022-07-04 00:26:31 +00:00
typedef struct {
float view [ 16 ] ;
float projection [ 16 ] ;
float viewProjection [ 16 ] ;
float inverseProjection [ 16 ] ;
} Camera ;
typedef struct {
2022-08-03 05:00:11 +00:00
bool dirty ;
MeshMode mode ;
float color [ 4 ] ;
2022-07-04 00:26:31 +00:00
float viewport [ 4 ] ;
float depthRange [ 2 ] ;
uint32_t scissor [ 4 ] ;
2022-08-03 05:00:11 +00:00
uint64_t formatHash ;
gpu_pipeline_info info ;
Material * material ;
Sampler * sampler ;
Shader * shader ;
Font * font ;
2022-07-04 00:26:31 +00:00
} Pipeline ;
2022-07-12 03:52:35 +00:00
enum {
SHAPE_PLANE ,
SHAPE_BOX ,
SHAPE_CIRCLE ,
SHAPE_SPHERE ,
SHAPE_CYLINDER ,
2022-07-17 23:38:00 +00:00
SHAPE_CONE ,
2022-07-12 03:52:35 +00:00
SHAPE_CAPSULE ,
SHAPE_TORUS ,
SHAPE_MONKEY
} ;
typedef struct {
uint64_t hash ;
gpu_buffer * vertices ;
gpu_buffer * indices ;
} Shape ;
2022-06-04 21:28:23 +00:00
typedef struct {
Sync * sync ;
Buffer * buffer ;
Texture * texture ;
gpu_phase phase ;
gpu_cache cache ;
} Access ;
2022-04-29 05:30:31 +00:00
struct Pass {
uint32_t ref ;
2022-08-03 05:00:11 +00:00
uint32_t tick ;
2022-04-29 05:30:31 +00:00
PassInfo info ;
2022-08-03 05:00:11 +00:00
gpu_pass * gpu ;
Texture * color [ 4 ] ;
Texture * depth ;
bool resolve ;
gpu_render_target target ;
2022-04-29 05:30:31 +00:00
gpu_stream * stream ;
2022-05-07 00:26:38 +00:00
float * transform ;
float transforms [ 16 ] [ 16 ] ;
2022-05-11 19:50:26 +00:00
uint32_t transformIndex ;
Pipeline * pipeline ;
Pipeline pipelines [ 4 ] ;
uint32_t pipelineIndex ;
2022-06-08 03:42:10 +00:00
bool samplerDirty ;
2022-06-17 06:49:09 +00:00
bool materialDirty ;
2022-05-30 22:36:31 +00:00
char constants [ 256 ] ;
bool constantsDirty ;
2022-05-24 05:32:36 +00:00
gpu_binding bindings [ 32 ] ;
uint32_t bindingMask ;
bool bindingsDirty ;
2022-05-30 19:29:00 +00:00
Camera * cameras ;
uint32_t cameraCount ;
bool cameraDirty ;
2022-05-30 22:36:31 +00:00
DrawData * drawData ;
uint32_t drawCount ;
2022-08-06 20:37:27 +00:00
gpu_binding builtins [ 4 ] ;
2022-05-30 22:36:31 +00:00
gpu_buffer * vertexBuffer ;
gpu_buffer * indexBuffer ;
2022-07-12 03:52:35 +00:00
Shape shapeCache [ 16 ] ;
2022-07-31 20:02:41 +00:00
arr_t ( Readback * ) readbacks ;
2022-06-04 21:28:23 +00:00
arr_t ( Access ) access ;
2022-04-29 05:30:31 +00:00
} ;
2022-06-17 06:49:09 +00:00
typedef struct {
Material * list ;
gpu_buffer * buffer ;
void * pointer ;
gpu_bundle_pool * bundlePool ;
gpu_bundle * bundles ;
uint32_t head ;
uint32_t tail ;
} MaterialBlock ;
2022-05-24 04:40:57 +00:00
typedef struct {
void * next ;
gpu_bundle_pool * gpu ;
gpu_bundle * bundles ;
uint32_t cursor ;
uint32_t tick ;
} BundlePool ;
typedef struct {
uint64_t hash ;
gpu_layout * gpu ;
BundlePool * head ;
BundlePool * tail ;
} Layout ;
2022-04-27 05:44:44 +00:00
typedef struct {
char * memory ;
2022-08-07 01:05:30 +00:00
size_t cursor ;
size_t length ;
2022-04-27 05:44:44 +00:00
} Allocator ;
2019-06-27 08:47:08 +00:00
static struct {
bool initialized ;
2022-04-29 05:30:31 +00:00
bool active ;
uint32_t tick ;
2022-06-12 05:55:43 +00:00
gpu_stream * stream ;
2022-06-12 02:07:46 +00:00
bool hasTextureUpload ;
2022-06-17 06:49:09 +00:00
bool hasMaterialUpload ;
2022-06-21 01:26:15 +00:00
bool hasGlyphUpload ;
2022-07-04 00:26:31 +00:00
bool hasReskin ;
2022-04-22 20:28:59 +00:00
gpu_device_info device ;
2022-04-21 09:16:17 +00:00
gpu_features features ;
gpu_limits limits ;
2022-08-06 04:05:02 +00:00
float background [ 4 ] ;
2022-05-11 22:28:04 +00:00
Texture * window ;
2022-08-03 05:00:11 +00:00
Pass * windowPass ;
2022-06-21 01:26:15 +00:00
Font * defaultFont ;
2022-05-24 05:32:12 +00:00
Buffer * defaultBuffer ;
Texture * defaultTexture ;
2022-06-08 03:42:10 +00:00
Sampler * defaultSamplers [ 2 ] ;
2022-07-04 00:26:31 +00:00
Shader * animator ;
2022-07-01 01:46:15 +00:00
Shader * timeWizard ;
2022-05-28 03:47:07 +00:00
Shader * defaultShaders [ DEFAULT_SHADER_COUNT ] ;
2022-07-15 02:23:02 +00:00
gpu_vertex_format vertexFormats [ VERTEX_FORMAX ] ;
2022-07-14 07:05:58 +00:00
Readback * oldestReadback ;
Readback * newestReadback ;
2022-06-17 06:49:09 +00:00
Material * defaultMaterial ;
2022-08-07 01:05:30 +00:00
size_t materialBlock ;
2022-06-17 06:49:09 +00:00
arr_t ( MaterialBlock ) materialBlocks ;
2022-05-24 04:44:42 +00:00
map_t pipelineLookup ;
arr_t ( gpu_pipeline * ) pipelines ;
2022-05-24 04:40:57 +00:00
arr_t ( Layout ) layouts ;
2022-08-07 01:05:30 +00:00
size_t builtinLayout ;
size_t materialLayout ;
2022-04-27 05:44:44 +00:00
Allocator allocator ;
2019-06-27 08:47:08 +00:00
} state ;
2016-07-07 07:04:24 +00:00
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-29 05:30:31 +00:00
static void * tempAlloc ( size_t size ) ;
2022-08-07 01:05:30 +00:00
static size_t tempPush ( void ) ;
static void tempPop ( size_t stack ) ;
2022-07-04 00:26:31 +00:00
static int u64cmp ( const void * a , const void * b ) ;
2022-04-29 05:30:31 +00:00
static void beginFrame ( void ) ;
2022-07-14 07:05:58 +00:00
static void processReadbacks ( void ) ;
2022-08-07 01:05:30 +00:00
static size_t getLayout ( gpu_slot * slots , uint32_t count ) ;
static gpu_bundle * getBundle ( size_t layout ) ;
2022-08-03 05:00:11 +00:00
static uint32_t convertTextureUsage ( uint32_t usage ) ;
2022-08-07 01:05:30 +00:00
static uint32_t measureTexture ( TextureFormat format , uint32_t w , uint32_t h , uint32_t d ) ;
2022-05-26 06:52:24 +00:00
static void checkTextureBounds ( const TextureInfo * info , uint32_t offset [ 4 ] , uint32_t extent [ 3 ] ) ;
2022-06-23 02:05:36 +00:00
static void mipmapTexture ( gpu_stream * stream , Texture * texture , uint32_t base , uint32_t count ) ;
2022-06-04 21:28:23 +00:00
static ShaderResource * findShaderResource ( Shader * shader , const char * name , size_t length , uint32_t slot ) ;
static void trackBuffer ( Pass * pass , Buffer * buffer , gpu_phase phase , gpu_cache cache ) ;
static void trackTexture ( Pass * pass , Texture * texture , gpu_phase phase , gpu_cache cache ) ;
2022-08-06 07:50:25 +00:00
static void trackMaterial ( Pass * pass , Material * material , gpu_phase phase , gpu_cache cache ) ;
2022-07-04 00:26:31 +00:00
static void updateModelTransforms ( Model * model , uint32_t nodeIndex , float * parent ) ;
2022-05-22 22:09:09 +00:00
static void checkShaderFeatures ( uint32_t * features , uint32_t count ) ;
2022-04-21 07:27:13 +00:00
static void onMessage ( void * context , const char * message , bool severe ) ;
// Entry
2022-08-03 05:00:11 +00:00
bool lovrGraphicsInit ( GraphicsConfig * config ) {
2022-04-19 02:30:58 +00:00
if ( state . initialized ) return false ;
2022-04-21 07:27:13 +00:00
2022-08-03 05:00:11 +00:00
gpu_config gpu = {
. debug = config - > debug ,
2022-04-21 09:16:17 +00:00
. callback = onMessage ,
2022-04-27 07:21:04 +00:00
. engineName = " LOVR " ,
. engineVersion = { LOVR_VERSION_MAJOR , LOVR_VERSION_MINOR , LOVR_VERSION_PATCH } ,
2022-04-21 09:16:17 +00:00
. device = & state . device ,
. features = & state . features ,
. limits = & state . limits
2022-04-21 07:27:13 +00:00
} ;
2022-05-11 22:28:04 +00:00
# ifdef LOVR_VK
2022-08-03 05:05:12 +00:00
gpu . vk . cacheData = config - > cacheData ;
gpu . vk . cacheSize = config - > cacheSize ;
2022-05-11 22:28:04 +00:00
if ( os_window_is_open ( ) ) {
2022-08-03 05:00:11 +00:00
gpu . vk . getInstanceExtensions = os_vk_get_instance_extensions ;
gpu . vk . createSurface = os_vk_create_surface ;
gpu . vk . surface = true ;
gpu . vk . vsync = config - > vsync ;
2022-05-11 22:28:04 +00:00
}
# endif
2022-06-06 03:38:14 +00:00
# if defined LOVR_VK && !defined LOVR_DISABLE_HEADSET
if ( lovrHeadsetInterface ) {
2022-08-03 05:00:11 +00:00
gpu . vk . getPhysicalDevice = lovrHeadsetInterface - > getVulkanPhysicalDevice ;
gpu . vk . createInstance = lovrHeadsetInterface - > createVulkanInstance ;
gpu . vk . createDevice = lovrHeadsetInterface - > createVulkanDevice ;
2022-06-06 03:38:14 +00:00
}
# endif
2022-08-03 05:00:11 +00:00
if ( ! gpu_init ( & gpu ) ) {
2022-04-21 07:27:13 +00:00
lovrThrow ( " Failed to initialize GPU " ) ;
}
2022-07-04 22:21:43 +00:00
lovrAssert ( state . limits . uniformBufferRange > = 65536 , " LÖVR requires the GPU to support a uniform buffer range of at least 64KB " ) ;
2022-06-01 04:41:43 +00:00
// Temporary frame memory uses a large 1GB virtual memory allocation, committing pages as needed
state . allocator . length = 1 < < 14 ;
state . allocator . memory = os_vm_init ( MAX_FRAME_MEMORY ) ;
os_vm_commit ( state . allocator . memory , state . allocator . length ) ;
2022-05-24 04:44:42 +00:00
map_init ( & state . pipelineLookup , 64 ) ;
arr_init ( & state . pipelines , realloc ) ;
2022-05-24 04:40:57 +00:00
arr_init ( & state . layouts , realloc ) ;
2022-06-17 06:49:09 +00:00
arr_init ( & state . materialBlocks , realloc ) ;
2022-05-24 04:40:57 +00:00
2022-06-17 06:49:09 +00:00
gpu_slot builtinSlots [ ] = {
2022-08-06 20:37:27 +00:00
{ 0 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_ALL } , // Globals
{ 1 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_ALL } , // Cameras
{ 2 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_ALL } , // Draw data
{ 3 , GPU_SLOT_SAMPLER , GPU_STAGE_ALL } , // Default sampler
2022-05-24 04:40:57 +00:00
} ;
2022-06-17 06:49:09 +00:00
state . builtinLayout = getLayout ( builtinSlots , COUNTOF ( builtinSlots ) ) ;
gpu_slot materialSlots [ ] = {
{ 0 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Data
{ 1 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Color
{ 2 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Glow
{ 3 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Occlusion
{ 4 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Metalness
{ 5 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Roughness
{ 6 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } , // Clearcoat
{ 7 , GPU_SLOT_SAMPLED_TEXTURE , GPU_STAGE_VERTEX | GPU_STAGE_FRAGMENT } // Normal
} ;
state . materialLayout = getLayout ( materialSlots , COUNTOF ( materialSlots ) ) ;
2022-05-24 04:40:57 +00:00
2022-06-19 06:31:51 +00:00
float data [ ] = { 0.f , 0.f , 0.f , 0.f , 1.f , 1.f , 1.f , 1.f } ;
2022-06-01 04:41:43 +00:00
state . defaultBuffer = lovrBufferCreate ( & ( BufferInfo ) {
2022-06-19 06:31:51 +00:00
. length = sizeof ( data ) ,
2022-05-24 05:32:12 +00:00
. stride = 1 ,
. label = " Default Buffer "
2022-06-01 04:41:43 +00:00
} , NULL ) ;
2022-05-24 05:32:12 +00:00
2022-06-19 06:31:51 +00:00
beginFrame ( ) ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
float * pointer = gpu_map ( scratchpad , sizeof ( data ) , 4 , GPU_MAP_WRITE ) ;
memcpy ( pointer , data , sizeof ( data ) ) ;
gpu_copy_buffers ( state . stream , scratchpad , state . defaultBuffer - > gpu , 0 , 0 , sizeof ( data ) ) ;
2022-06-04 21:54:04 +00:00
Image * image = lovrImageCreateRaw ( 4 , 4 , FORMAT_RGBA8 ) ;
float white [ 4 ] = { 1.f , 1.f , 1.f , 1.f } ;
for ( uint32_t y = 0 ; y < 4 ; y + + ) {
for ( uint32_t x = 0 ; x < 4 ; x + + ) {
lovrImageSetPixel ( image , x , y , white ) ;
}
}
2022-06-01 04:41:43 +00:00
state . defaultTexture = lovrTextureCreate ( & ( TextureInfo ) {
2022-05-24 05:32:12 +00:00
. type = TEXTURE_2D ,
2022-06-04 21:54:04 +00:00
. usage = TEXTURE_SAMPLE ,
2022-05-24 05:32:12 +00:00
. format = FORMAT_RGBA8 ,
. width = 4 ,
. height = 4 ,
2022-07-30 22:08:30 +00:00
. layers = 1 ,
2022-05-24 05:32:12 +00:00
. mipmaps = 1 ,
. samples = 1 ,
. srgb = true ,
2022-06-04 21:54:04 +00:00
. imageCount = 1 ,
. images = & image ,
2022-05-24 05:32:12 +00:00
. label = " Default Texture "
2022-06-01 04:41:43 +00:00
} ) ;
2022-05-24 05:32:12 +00:00
2022-06-04 21:54:04 +00:00
lovrRelease ( image , lovrImageDestroy ) ;
2022-06-08 03:42:10 +00:00
for ( uint32_t i = 0 ; i < 2 ; i + + ) {
state . defaultSamplers [ i ] = lovrSamplerCreate ( & ( SamplerInfo ) {
. min = i = = 0 ? FILTER_NEAREST : FILTER_LINEAR ,
. mag = i = = 0 ? FILTER_NEAREST : FILTER_LINEAR ,
. mip = i = = 0 ? FILTER_NEAREST : FILTER_LINEAR ,
. wrap = { WRAP_REPEAT , WRAP_REPEAT , WRAP_REPEAT }
} ) ;
}
2022-05-24 05:32:12 +00:00
2022-06-16 03:46:43 +00:00
state . vertexFormats [ VERTEX_SHAPE ] = ( gpu_vertex_format ) {
2022-06-04 08:34:13 +00:00
. bufferCount = 2 ,
2022-06-16 03:46:43 +00:00
. attributeCount = 5 ,
2022-06-19 06:31:51 +00:00
. bufferStrides [ 0 ] = sizeof ( ShapeVertex ) ,
. attributes [ 0 ] = { 0 , 10 , offsetof ( ShapeVertex , position ) , GPU_TYPE_F32x3 } ,
. attributes [ 1 ] = { 0 , 11 , offsetof ( ShapeVertex , normal ) , GPU_TYPE_F32x3 } ,
. attributes [ 2 ] = { 0 , 12 , offsetof ( ShapeVertex , uv ) , GPU_TYPE_F32x2 } ,
. attributes [ 3 ] = { 1 , 13 , 16 , GPU_TYPE_F32x4 } ,
. attributes [ 4 ] = { 1 , 14 , 0 , GPU_TYPE_F32x4 }
2022-06-04 08:34:13 +00:00
} ;
2022-06-16 03:46:43 +00:00
state . vertexFormats [ VERTEX_POINT ] = ( gpu_vertex_format ) {
2022-06-01 04:20:01 +00:00
. bufferCount = 2 ,
2022-06-19 00:43:12 +00:00
. attributeCount = 5 ,
2022-06-21 01:26:15 +00:00
. bufferStrides [ 0 ] = 12 ,
2022-06-19 06:31:51 +00:00
. attributes [ 0 ] = { 0 , 10 , 0 , GPU_TYPE_F32x3 } ,
. attributes [ 1 ] = { 1 , 11 , 0 , GPU_TYPE_F32x4 } ,
. attributes [ 2 ] = { 1 , 12 , 0 , GPU_TYPE_F32x4 } ,
. attributes [ 3 ] = { 1 , 13 , 16 , GPU_TYPE_F32x4 } ,
. attributes [ 4 ] = { 1 , 14 , 0 , GPU_TYPE_F32x4 }
2022-05-30 22:36:31 +00:00
} ;
2022-06-19 00:43:12 +00:00
state . vertexFormats [ VERTEX_GLYPH ] = ( gpu_vertex_format ) {
. bufferCount = 2 ,
. attributeCount = 5 ,
2022-06-21 01:26:15 +00:00
. bufferStrides [ 0 ] = sizeof ( GlyphVertex ) ,
2022-06-19 06:31:51 +00:00
. attributes [ 0 ] = { 0 , 10 , offsetof ( GlyphVertex , position ) , GPU_TYPE_F32x2 } ,
. attributes [ 1 ] = { 1 , 11 , 0 , GPU_TYPE_F32x4 } ,
2022-06-27 03:28:30 +00:00
. attributes [ 2 ] = { 0 , 12 , offsetof ( GlyphVertex , uv ) , GPU_TYPE_UN16x2 } ,
. attributes [ 3 ] = { 0 , 13 , offsetof ( GlyphVertex , color ) , GPU_TYPE_UN8x4 } ,
2022-06-19 06:31:51 +00:00
. attributes [ 4 ] = { 1 , 14 , 0 , GPU_TYPE_F32x4 }
2022-06-19 00:43:12 +00:00
} ;
2022-07-04 00:26:31 +00:00
state . vertexFormats [ VERTEX_MODEL ] = ( gpu_vertex_format ) {
. bufferCount = 2 ,
. attributeCount = 5 ,
. bufferStrides [ 0 ] = sizeof ( ModelVertex ) ,
. attributes [ 0 ] = { 0 , 10 , offsetof ( ModelVertex , position ) , GPU_TYPE_F32x3 } ,
. attributes [ 1 ] = { 0 , 11 , offsetof ( ModelVertex , normal ) , GPU_TYPE_F32x3 } ,
. attributes [ 2 ] = { 0 , 12 , offsetof ( ModelVertex , uv ) , GPU_TYPE_F32x2 } ,
. attributes [ 3 ] = { 0 , 13 , offsetof ( ModelVertex , color ) , GPU_TYPE_UN8x4 } ,
. attributes [ 4 ] = { 0 , 14 , offsetof ( ModelVertex , tangent ) , GPU_TYPE_F32x3 }
} ;
2022-06-25 02:59:48 +00:00
state . vertexFormats [ VERTEX_EMPTY ] = ( gpu_vertex_format ) {
. bufferCount = 2 ,
. attributeCount = 5 ,
. attributes [ 0 ] = { 1 , 10 , 0 , GPU_TYPE_F32x2 } ,
. attributes [ 1 ] = { 1 , 11 , 0 , GPU_TYPE_F32x4 } ,
. attributes [ 2 ] = { 1 , 12 , 0 , GPU_TYPE_F32x2 } ,
. attributes [ 3 ] = { 1 , 13 , 16 , GPU_TYPE_F32x4 } ,
. attributes [ 4 ] = { 1 , 14 , 0 , GPU_TYPE_F32x4 }
} ;
2022-06-17 06:49:09 +00:00
state . defaultMaterial = lovrMaterialCreate ( & ( MaterialInfo ) {
2022-06-19 06:31:51 +00:00
. data . color = { 1.f , 1.f , 1.f , 1.f } ,
. texture = state . defaultTexture
2022-06-17 06:49:09 +00:00
} ) ;
2022-08-03 05:00:11 +00:00
if ( gpu . vk . surface ) {
state . window = malloc ( sizeof ( Texture ) ) ;
lovrAssert ( state . window , " Out of memory " ) ;
int width , height ;
os_window_get_fbsize ( & width , & height ) ;
state . window - > ref = 1 ;
state . window - > gpu = NULL ;
state . window - > renderView = NULL ;
state . window - > info = ( TextureInfo ) {
. type = TEXTURE_2D ,
. format = GPU_FORMAT_SURFACE ,
. width = width ,
. height = height ,
. layers = 1 ,
. mipmaps = 1 ,
. samples = 1 ,
. usage = TEXTURE_RENDER ,
. srgb = true
} ;
TextureFormat depthFormat = config - > stencil ? FORMAT_D32FS8 : FORMAT_D32F ;
if ( config - > stencil & & ! lovrGraphicsIsFormatSupported ( depthFormat , TEXTURE_FEATURE_RENDER ) ) {
depthFormat = FORMAT_D24S8 ; // Guaranteed to be supported if the other one isn't
}
state . windowPass = lovrPassCreate ( & ( PassInfo ) {
. type = PASS_RENDER ,
. canvas . count = 1 ,
. canvas . textures [ 0 ] = state . window ,
. canvas . depth . format = depthFormat ,
. canvas . samples = config - > antialias ? 4 : 1
} ) ;
}
2022-06-01 04:45:54 +00:00
float16Init ( ) ;
glslang_initialize_process ( ) ;
2022-04-19 02:30:58 +00:00
state . initialized = true ;
return true ;
2016-09-28 03:20:08 +00:00
}
2016-10-04 22:13:57 +00:00
void lovrGraphicsDestroy ( ) {
2018-02-23 03:18:36 +00:00
if ( ! state . initialized ) return ;
2022-07-15 02:23:02 +00:00
for ( Readback * readback = state . oldestReadback ; readback ; readback = readback - > next ) {
lovrRelease ( readback , lovrReadbackDestroy ) ;
}
2022-06-01 04:45:54 +00:00
lovrRelease ( state . window , lovrTextureDestroy ) ;
2022-08-03 05:00:11 +00:00
lovrRelease ( state . windowPass , lovrPassDestroy ) ;
2022-06-21 01:26:15 +00:00
lovrRelease ( state . defaultFont , lovrFontDestroy ) ;
2022-06-01 04:45:54 +00:00
lovrRelease ( state . defaultBuffer , lovrBufferDestroy ) ;
lovrRelease ( state . defaultTexture , lovrTextureDestroy ) ;
2022-06-08 03:42:10 +00:00
lovrRelease ( state . defaultSamplers [ 0 ] , lovrSamplerDestroy ) ;
lovrRelease ( state . defaultSamplers [ 1 ] , lovrSamplerDestroy ) ;
2022-07-04 00:26:31 +00:00
lovrRelease ( state . animator , lovrShaderDestroy ) ;
2022-07-01 01:46:15 +00:00
lovrRelease ( state . timeWizard , lovrShaderDestroy ) ;
2022-08-07 01:05:30 +00:00
for ( size_t i = 0 ; i < COUNTOF ( state . defaultShaders ) ; i + + ) {
2022-06-01 04:45:54 +00:00
lovrRelease ( state . defaultShaders [ i ] , lovrShaderDestroy ) ;
}
2022-06-17 06:49:09 +00:00
lovrRelease ( state . defaultMaterial , lovrMaterialDestroy ) ;
2022-08-07 01:05:30 +00:00
for ( size_t i = 0 ; i < state . materialBlocks . length ; i + + ) {
2022-06-17 06:49:09 +00:00
MaterialBlock * block = & state . materialBlocks . data [ i ] ;
gpu_buffer_destroy ( block - > buffer ) ;
gpu_bundle_pool_destroy ( block - > bundlePool ) ;
free ( block - > list ) ;
free ( block - > buffer ) ;
free ( block - > bundlePool ) ;
free ( block - > bundles ) ;
}
arr_free ( & state . materialBlocks ) ;
2022-08-07 01:05:30 +00:00
for ( size_t i = 0 ; i < state . pipelines . length ; i + + ) {
2022-05-24 04:44:42 +00:00
gpu_pipeline_destroy ( state . pipelines . data [ i ] ) ;
free ( state . pipelines . data [ i ] ) ;
}
2022-06-01 04:45:54 +00:00
map_free ( & state . pipelineLookup ) ;
arr_free ( & state . pipelines ) ;
2022-08-07 01:05:30 +00:00
for ( size_t i = 0 ; i < state . layouts . length ; i + + ) {
2022-05-30 20:12:04 +00:00
BundlePool * pool = state . layouts . data [ i ] . head ;
while ( pool ) {
BundlePool * next = pool - > next ;
gpu_bundle_pool_destroy ( pool - > gpu ) ;
free ( pool - > gpu ) ;
free ( pool - > bundles ) ;
free ( pool ) ;
pool = next ;
}
2022-05-24 04:40:57 +00:00
gpu_layout_destroy ( state . layouts . data [ i ] . gpu ) ;
free ( state . layouts . data [ i ] . gpu ) ;
}
arr_free ( & state . layouts ) ;
2022-04-21 07:27:13 +00:00
gpu_destroy ( ) ;
2022-05-09 18:47:06 +00:00
glslang_finalize_process ( ) ;
2022-04-27 05:44:44 +00:00
os_vm_free ( state . allocator . memory , MAX_FRAME_MEMORY ) ;
2019-06-27 08:47:08 +00:00
memset ( & state , 0 , sizeof ( state ) ) ;
2016-07-07 07:04:24 +00:00
}
2022-04-21 07:27:13 +00:00
2022-04-21 09:16:17 +00:00
void lovrGraphicsGetDevice ( GraphicsDevice * device ) {
2022-04-22 20:28:59 +00:00
device - > deviceId = state . device . deviceId ;
device - > vendorId = state . device . vendorId ;
device - > name = state . device . deviceName ;
2022-04-21 09:16:17 +00:00
device - > renderer = state . device . renderer ;
device - > subgroupSize = state . device . subgroupSize ;
2022-04-22 20:28:59 +00:00
device - > discrete = state . device . discrete ;
2022-04-21 09:16:17 +00:00
}
void lovrGraphicsGetFeatures ( GraphicsFeatures * features ) {
2022-04-26 22:31:51 +00:00
features - > textureBC = state . features . textureBC ;
features - > textureASTC = state . features . textureASTC ;
2022-04-21 09:16:17 +00:00
features - > wireframe = state . features . wireframe ;
features - > depthClamp = state . features . depthClamp ;
features - > indirectDrawFirstInstance = state . features . indirectDrawFirstInstance ;
features - > float64 = state . features . float64 ;
features - > int64 = state . features . int64 ;
features - > int16 = state . features . int16 ;
}
void lovrGraphicsGetLimits ( GraphicsLimits * limits ) {
limits - > textureSize2D = state . limits . textureSize2D ;
limits - > textureSize3D = state . limits . textureSize3D ;
limits - > textureSizeCube = state . limits . textureSizeCube ;
limits - > textureLayers = state . limits . textureLayers ;
limits - > renderSize [ 0 ] = state . limits . renderSize [ 0 ] ;
limits - > renderSize [ 1 ] = state . limits . renderSize [ 1 ] ;
limits - > renderSize [ 2 ] = state . limits . renderSize [ 2 ] ;
2022-07-04 22:54:43 +00:00
limits - > uniformBuffersPerStage = MIN ( state . limits . uniformBuffersPerStage - 3 , MAX_SHADER_RESOURCES ) ;
2022-06-04 21:28:23 +00:00
limits - > storageBuffersPerStage = MIN ( state . limits . storageBuffersPerStage , MAX_SHADER_RESOURCES ) ;
2022-07-04 22:54:43 +00:00
limits - > sampledTexturesPerStage = MIN ( state . limits . sampledTexturesPerStage - 7 , MAX_SHADER_RESOURCES ) ;
2022-06-04 21:28:23 +00:00
limits - > storageTexturesPerStage = MIN ( state . limits . storageTexturesPerStage , MAX_SHADER_RESOURCES ) ;
limits - > samplersPerStage = MIN ( state . limits . samplersPerStage - 1 , MAX_SHADER_RESOURCES ) ;
limits - > resourcesPerShader = MAX_SHADER_RESOURCES ;
2022-04-21 09:16:17 +00:00
limits - > uniformBufferRange = state . limits . uniformBufferRange ;
limits - > storageBufferRange = state . limits . storageBufferRange ;
limits - > uniformBufferAlign = state . limits . uniformBufferAlign ;
limits - > storageBufferAlign = state . limits . storageBufferAlign ;
2022-06-16 03:46:43 +00:00
limits - > vertexAttributes = 10 ;
2022-04-21 09:16:17 +00:00
limits - > vertexBufferStride = state . limits . vertexBufferStride ;
2022-06-16 03:46:43 +00:00
limits - > vertexShaderOutputs = 10 ;
2022-04-26 22:31:51 +00:00
limits - > clipDistances = state . limits . clipDistances ;
limits - > cullDistances = state . limits . cullDistances ;
limits - > clipAndCullDistances = state . limits . clipAndCullDistances ;
2022-08-06 20:06:42 +00:00
memcpy ( limits - > workgroupCount , state . limits . workgroupCount , 3 * sizeof ( uint32_t ) ) ;
memcpy ( limits - > workgroupSize , state . limits . workgroupSize , 3 * sizeof ( uint32_t ) ) ;
limits - > totalWorkgroupSize = state . limits . totalWorkgroupSize ;
2022-04-21 09:16:17 +00:00
limits - > computeSharedMemory = state . limits . computeSharedMemory ;
2022-05-30 22:36:31 +00:00
limits - > shaderConstantSize = MIN ( state . limits . pushConstantSize , 256 ) ;
2022-04-21 09:16:17 +00:00
limits - > indirectDrawCount = state . limits . indirectDrawCount ;
limits - > instances = state . limits . instances ;
limits - > anisotropy = state . limits . anisotropy ;
limits - > pointSize = state . limits . pointSize ;
}
2022-04-30 00:12:10 +00:00
bool lovrGraphicsIsFormatSupported ( uint32_t format , uint32_t features ) {
uint8_t supports = state . features . formats [ format ] ;
if ( ! features ) return supports ;
if ( ( features & TEXTURE_FEATURE_SAMPLE ) & & ! ( supports & GPU_FEATURE_SAMPLE ) ) return false ;
if ( ( features & TEXTURE_FEATURE_FILTER ) & & ! ( supports & GPU_FEATURE_FILTER ) ) return false ;
if ( ( features & TEXTURE_FEATURE_RENDER ) & & ! ( supports & GPU_FEATURE_RENDER ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLEND ) & & ! ( supports & GPU_FEATURE_BLEND ) ) return false ;
if ( ( features & TEXTURE_FEATURE_STORAGE ) & & ! ( supports & GPU_FEATURE_STORAGE ) ) return false ;
if ( ( features & TEXTURE_FEATURE_ATOMIC ) & & ! ( supports & GPU_FEATURE_ATOMIC ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLIT_SRC ) & & ! ( supports & GPU_FEATURE_BLIT_SRC ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLIT_DST ) & & ! ( supports & GPU_FEATURE_BLIT_DST ) ) return false ;
return true ;
}
2022-08-03 05:05:12 +00:00
void lovrGraphicsGetShaderCache ( void * data , size_t * size ) {
gpu_pipeline_get_cache ( data , size ) ;
}
2022-05-11 22:38:01 +00:00
2022-08-06 04:05:02 +00:00
void lovrGraphicsGetBackground ( float background [ 4 ] ) {
background [ 0 ] = lovrMathLinearToGamma ( state . background [ 0 ] ) ;
background [ 1 ] = lovrMathLinearToGamma ( state . background [ 1 ] ) ;
background [ 2 ] = lovrMathLinearToGamma ( state . background [ 2 ] ) ;
background [ 3 ] = state . background [ 3 ] ;
}
void lovrGraphicsSetBackground ( float background [ 4 ] ) {
state . background [ 0 ] = lovrMathGammaToLinear ( background [ 0 ] ) ;
state . background [ 1 ] = lovrMathGammaToLinear ( background [ 1 ] ) ;
state . background [ 2 ] = lovrMathGammaToLinear ( background [ 2 ] ) ;
state . background [ 3 ] = background [ 3 ] ;
}
2022-04-29 05:30:31 +00:00
void lovrGraphicsSubmit ( Pass * * passes , uint32_t count ) {
if ( ! state . active ) {
return ;
}
2022-06-12 05:55:43 +00:00
uint32_t total = count + 1 ;
gpu_stream * * streams = tempAlloc ( total * sizeof ( gpu_stream * ) ) ;
2022-06-12 02:07:46 +00:00
gpu_barrier * barriers = tempAlloc ( count * sizeof ( gpu_barrier ) ) ;
memset ( barriers , 0 , count * sizeof ( gpu_barrier ) ) ;
2022-04-29 05:30:31 +00:00
2022-06-12 05:55:43 +00:00
streams [ 0 ] = state . stream ;
2022-06-12 02:07:46 +00:00
2022-06-12 05:55:43 +00:00
if ( state . hasTextureUpload ) {
barriers [ 0 ] . prev | = GPU_PHASE_TRANSFER ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_VERTEX ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_FRAGMENT ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_COMPUTE ;
barriers [ 0 ] . flush | = GPU_CACHE_TRANSFER_WRITE ;
barriers [ 0 ] . clear | = GPU_CACHE_TEXTURE ;
state . hasTextureUpload = false ;
2022-04-29 05:30:31 +00:00
}
2022-06-17 06:49:09 +00:00
if ( state . hasMaterialUpload ) {
barriers [ 0 ] . prev | = GPU_PHASE_TRANSFER ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_VERTEX ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_FRAGMENT ;
barriers [ 0 ] . flush | = GPU_CACHE_TRANSFER_WRITE ;
barriers [ 0 ] . clear | = GPU_CACHE_UNIFORM ;
state . hasMaterialUpload = false ;
}
2022-06-21 01:26:15 +00:00
if ( state . hasGlyphUpload ) {
barriers [ 0 ] . prev | = GPU_PHASE_TRANSFER ;
barriers [ 0 ] . next | = GPU_PHASE_SHADER_FRAGMENT ;
barriers [ 0 ] . flush | = GPU_CACHE_TRANSFER_WRITE ;
barriers [ 0 ] . clear | = GPU_CACHE_TEXTURE ;
state . hasGlyphUpload = false ;
}
2022-07-04 00:26:31 +00:00
if ( state . hasReskin ) {
barriers [ 0 ] . prev | = GPU_PHASE_SHADER_COMPUTE ;
barriers [ 0 ] . next | = GPU_PHASE_INPUT_VERTEX ;
barriers [ 0 ] . flush | = GPU_CACHE_STORAGE_WRITE ;
barriers [ 0 ] . clear | = GPU_CACHE_VERTEX ;
state . hasReskin = false ;
}
2022-08-03 05:00:11 +00:00
// Finish passes
2022-06-12 05:55:43 +00:00
for ( uint32_t i = 0 ; i < count ; i + + ) {
2022-07-31 20:02:41 +00:00
Pass * pass = passes [ i ] ;
2022-08-03 05:00:11 +00:00
lovrAssert ( passes [ i ] - > tick = = state . tick , " Trying to submit a Pass that wasn't reset this frame " ) ;
2022-07-31 20:02:41 +00:00
2022-08-08 05:28:12 +00:00
for ( uint32_t j = 0 ; j < i ; j + + ) {
lovrCheck ( passes [ j ] ! = passes [ i ] , " Using a Pass twice in the same submit is not allowed " ) ;
}
2022-07-31 20:02:41 +00:00
streams [ i + 1 ] = pass - > stream ;
switch ( pass - > info . type ) {
case PASS_RENDER :
gpu_render_end ( pass - > stream ) ;
2022-06-12 02:07:46 +00:00
2022-07-31 20:02:41 +00:00
Canvas * canvas = & pass - > info . canvas ;
2022-06-23 02:05:36 +00:00
2022-08-04 07:06:54 +00:00
if ( canvas - > mipmap ) {
for ( uint32_t j = 0 ; j < COUNTOF ( canvas - > textures ) & & canvas - > textures [ j ] ; j + + ) {
if ( canvas - > textures [ j ] - > info . mipmaps > 1 ) {
barriers [ i ] . prev | = GPU_PHASE_COLOR ;
barriers [ i ] . next | = GPU_PHASE_TRANSFER ;
barriers [ i ] . flush | = GPU_CACHE_COLOR_WRITE ;
barriers [ i ] . clear | = GPU_CACHE_TRANSFER_READ ;
mipmapTexture ( pass - > stream , canvas - > textures [ j ] , 0 , ~ 0u ) ;
}
2022-07-31 20:02:41 +00:00
}
2022-06-23 02:05:36 +00:00
2022-08-04 07:06:54 +00:00
if ( canvas - > depth . texture & & canvas - > depth . texture - > info . mipmaps > 1 ) {
barriers [ i ] . prev | = GPU_PHASE_DEPTH_LATE ;
barriers [ i ] . next | = GPU_PHASE_TRANSFER ;
barriers [ i ] . flush | = GPU_CACHE_DEPTH_WRITE ;
barriers [ i ] . clear | = GPU_CACHE_TRANSFER_READ ;
mipmapTexture ( pass - > stream , canvas - > depth . texture , 0 , ~ 0u ) ;
2022-07-31 20:02:41 +00:00
}
}
break ;
case PASS_COMPUTE :
gpu_compute_end ( pass - > stream ) ;
break ;
case PASS_TRANSFER :
for ( uint32_t j = 0 ; j < pass - > readbacks . length ; j + + ) {
Readback * readback = pass - > readbacks . data [ j ] ;
2022-07-06 04:58:54 +00:00
2022-07-31 20:02:41 +00:00
if ( ! state . oldestReadback ) {
state . oldestReadback = readback ;
}
2022-06-23 02:05:36 +00:00
2022-07-31 20:02:41 +00:00
if ( state . newestReadback ) {
state . newestReadback - > next = readback ;
}
state . newestReadback = readback ;
lovrRetain ( readback ) ;
}
break ;
2022-05-11 19:51:13 +00:00
}
2022-04-29 05:30:31 +00:00
}
2022-06-12 05:55:43 +00:00
// Synchronization
for ( uint32_t i = 0 ; i < count ; i + + ) {
Pass * pass = passes [ i ] ;
2022-06-18 00:43:26 +00:00
for ( size_t j = 0 ; j < pass - > access . length ; j + + ) {
2022-06-12 05:55:43 +00:00
// access is the incoming resource access performed by the pass
Access * access = & pass - > access . data [ j ] ;
// sync is the existing state of the resource at the time of the access
Sync * sync = access - > sync ;
// barrier is a barrier that will be emitted at the end of the last pass that wrote to the
// resource (or the first pass if the last write was in a previous frame). The barrier
// specifies 'phases' and 'caches'. The 'next' phase will wait for the 'prev' phase to
// finish. In between, the 'flush' cache is flushed and the 'clear' cache is cleared.
gpu_barrier * barrier = & barriers [ sync - > lastWriteIndex ] ;
2022-08-06 01:36:51 +00:00
// Only the first write in a pass is considered for a barrier (and there's no intra-pass sync)
2022-06-12 05:55:43 +00:00
if ( sync - > lastWriteIndex = = i + 1 ) {
continue ;
}
// There are 4 types of access patterns:
// - read after read:
// - no hazard, no barrier necessary
// - read after write:
// - needs execution dependency to ensure the read happens after the write
// - needs to flush the writes from the cache
// - needs to clear the cache for the read so it gets the new data
// - only needs to happen once for each type of read after a write (tracked by pendingReads)
// - if a second read happens, the first read would have already synchronized (transitive)
// - write after write:
// - needs execution dependency to ensure writes don't overlap
// - needs to flush and clear the cache
// - resets the 'last write' and clears pendingReads
// - write after read:
// - needs execution dependency to ensure write starts after read is finished
// - does not need to flush any caches
// - does clear the write cache
// - resets the 'last write' and clears pendingReads
uint32_t read = access - > cache & GPU_CACHE_READ ;
uint32_t write = access - > cache & GPU_CACHE_WRITE ;
uint32_t newReads = read & ~ sync - > pendingReads ;
bool hasNewReads = newReads | | ( access - > phase & ~ sync - > readPhase ) ;
bool readAfterWrite = read & & sync - > pendingWrite & & hasNewReads ;
bool writeAfterWrite = write & & sync - > pendingWrite & & ! sync - > pendingReads ;
bool writeAfterRead = write & & sync - > pendingReads ;
if ( readAfterWrite ) {
barrier - > prev | = sync - > writePhase ;
barrier - > next | = access - > phase ;
barrier - > flush | = sync - > pendingWrite ;
barrier - > clear | = newReads ;
sync - > readPhase | = access - > phase ;
sync - > pendingReads | = read ;
}
if ( writeAfterWrite ) {
barrier - > prev | = sync - > writePhase ;
barrier - > next | = access - > phase ;
barrier - > flush | = sync - > pendingWrite ;
barrier - > clear | = write ;
}
if ( writeAfterRead ) {
barrier - > prev | = sync - > readPhase ;
barrier - > next | = access - > phase ;
sync - > readPhase = 0 ;
sync - > pendingReads = 0 ;
}
if ( write ) {
sync - > writePhase = access - > phase ;
sync - > pendingWrite = write ;
sync - > lastWriteIndex = i + 1 ;
}
}
2022-06-12 02:07:46 +00:00
}
2022-08-07 06:25:49 +00:00
for ( uint32_t i = 0 ; i < count ; i + + ) {
gpu_sync ( streams [ i ] , & barriers [ i ] , 1 ) ;
}
2022-06-12 02:07:46 +00:00
for ( uint32_t i = 0 ; i < count ; i + + ) {
2022-06-12 05:55:43 +00:00
for ( uint32_t j = 0 ; j < passes [ i ] - > access . length ; j + + ) {
passes [ i ] - > access . data [ j ] . sync - > lastWriteIndex = 0 ;
2022-08-06 01:36:51 +00:00
// OpenXR swapchain texture layout transitions >__>
Texture * texture = passes [ i ] - > access . data [ j ] . texture ;
if ( texture & & texture - > info . xr & & texture - > xrTick ! = state . tick ) {
gpu_xr_acquire ( streams [ 0 ] , texture - > gpu ) ;
gpu_xr_release ( streams [ total - 1 ] , texture - > gpu ) ;
texture - > xrTick = state . tick ;
}
2022-06-12 05:55:43 +00:00
}
}
for ( uint32_t i = 0 ; i < total ; i + + ) {
2022-04-29 05:30:31 +00:00
gpu_stream_end ( streams [ i ] ) ;
}
2022-08-04 07:06:54 +00:00
gpu_submit ( streams , total ) ;
2022-06-12 02:07:46 +00:00
2022-08-04 07:06:54 +00:00
state . stream = NULL ;
state . active = false ;
}
void lovrGraphicsPresent ( ) {
if ( state . window - > gpu ) {
2022-06-12 02:07:46 +00:00
state . window - > gpu = NULL ;
state . window - > renderView = NULL ;
2022-08-04 07:06:54 +00:00
gpu_present ( ) ;
2022-06-12 02:07:46 +00:00
}
2022-04-29 05:30:31 +00:00
}
2022-04-29 05:37:03 +00:00
void lovrGraphicsWait ( ) {
2022-07-14 07:05:58 +00:00
gpu_wait_idle ( ) ;
2022-04-29 05:37:03 +00:00
}
2022-04-26 22:32:54 +00:00
// Buffer
2022-04-27 07:28:39 +00:00
Buffer * lovrGraphicsGetBuffer ( BufferInfo * info , void * * data ) {
2022-04-28 22:39:45 +00:00
uint32_t size = info - > length * info - > stride ;
2022-04-27 07:28:39 +00:00
lovrCheck ( size > 0 , " Buffer size can not be zero " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( size < = 1 < < 30 , " Max buffer size is 1GB " ) ;
2022-04-27 07:28:39 +00:00
2022-04-29 05:30:31 +00:00
Buffer * buffer = tempAlloc ( sizeof ( Buffer ) + gpu_sizeof_buffer ( ) ) ;
2022-04-27 07:28:39 +00:00
buffer - > ref = 1 ;
buffer - > size = size ;
buffer - > gpu = ( gpu_buffer * ) ( buffer + 1 ) ;
buffer - > info = * info ;
2022-06-16 03:46:43 +00:00
buffer - > hash = hash64 ( info - > fields , info - > fieldCount * sizeof ( BufferField ) ) ;
2022-04-27 07:28:39 +00:00
buffer - > pointer = gpu_map ( buffer - > gpu , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
if ( data ) {
* data = buffer - > pointer ;
}
return buffer ;
}
2022-07-13 07:07:15 +00:00
Buffer * lovrBufferCreate ( const BufferInfo * info , void * * data ) {
2022-04-28 22:39:45 +00:00
uint32_t size = info - > length * info - > stride ;
2022-04-26 22:32:54 +00:00
lovrCheck ( size > 0 , " Buffer size can not be zero " ) ;
lovrCheck ( size < = 1 < < 30 , " Max buffer size is 1GB " ) ;
Buffer * buffer = calloc ( 1 , sizeof ( Buffer ) + gpu_sizeof_buffer ( ) ) ;
lovrAssert ( buffer , " Out of memory " ) ;
buffer - > ref = 1 ;
buffer - > size = size ;
buffer - > gpu = ( gpu_buffer * ) ( buffer + 1 ) ;
buffer - > info = * info ;
2022-06-16 03:46:43 +00:00
buffer - > hash = hash64 ( info - > fields , info - > fieldCount * sizeof ( BufferField ) ) ;
2022-04-26 22:32:54 +00:00
gpu_buffer_init ( buffer - > gpu , & ( gpu_buffer_info ) {
. size = buffer - > size ,
. label = info - > label ,
. pointer = data
} ) ;
2022-04-27 07:28:39 +00:00
if ( data & & * data = = NULL ) {
2022-06-12 05:55:43 +00:00
beginFrame ( ) ;
2022-04-29 05:30:31 +00:00
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
2022-04-27 07:28:39 +00:00
* data = gpu_map ( scratchpad , size , 4 , GPU_MAP_WRITE ) ;
2022-06-12 05:55:43 +00:00
gpu_copy_buffers ( state . stream , scratchpad , buffer - > gpu , 0 , 0 , size ) ;
2022-06-12 02:07:46 +00:00
buffer - > sync . writePhase = GPU_PHASE_TRANSFER ;
2022-06-12 05:55:43 +00:00
buffer - > sync . pendingWrite = GPU_CACHE_TRANSFER_WRITE ;
2022-04-27 07:28:39 +00:00
}
2022-04-26 22:32:54 +00:00
return buffer ;
}
void lovrBufferDestroy ( void * ref ) {
Buffer * buffer = ref ;
2022-05-26 02:23:01 +00:00
if ( lovrBufferIsTemporary ( buffer ) ) return ;
2022-04-26 22:32:54 +00:00
gpu_buffer_destroy ( buffer - > gpu ) ;
free ( buffer ) ;
}
const BufferInfo * lovrBufferGetInfo ( Buffer * buffer ) {
return & buffer - > info ;
}
2022-04-27 07:28:39 +00:00
bool lovrBufferIsTemporary ( Buffer * buffer ) {
2022-05-26 02:23:01 +00:00
return buffer - > pointer ! = NULL ;
2022-04-27 07:28:39 +00:00
}
void * lovrBufferMap ( Buffer * buffer , uint32_t offset , uint32_t size ) {
2022-06-10 05:44:23 +00:00
lovrAssert ( buffer - > pointer , " This function can only be called on temporary buffers " ) ;
return buffer - > pointer + offset ;
2022-04-27 07:28:39 +00:00
}
void lovrBufferClear ( Buffer * buffer , uint32_t offset , uint32_t size ) {
2022-06-10 05:44:23 +00:00
lovrAssert ( buffer - > pointer , " This function can only be called on temporary buffers " ) ;
2022-04-29 05:30:31 +00:00
lovrCheck ( size % 4 = = 0 , " Buffer clear size must be a multiple of 4 " ) ;
lovrCheck ( offset % 4 = = 0 , " Buffer clear offset must be a multiple of 4 " ) ;
2022-04-27 07:28:39 +00:00
lovrCheck ( offset + size < = buffer - > size , " Tried to clear past the end of the Buffer " ) ;
2022-06-10 05:44:23 +00:00
memset ( buffer - > pointer + offset , 0 , size ) ;
2022-04-27 07:28:39 +00:00
}
2022-04-30 03:38:34 +00:00
// Texture
2022-05-11 22:28:04 +00:00
Texture * lovrGraphicsGetWindowTexture ( ) {
if ( ! state . window - > gpu ) {
beginFrame ( ) ;
state . window - > gpu = gpu_surface_acquire ( ) ;
state . window - > renderView = state . window - > gpu ;
}
return state . window ;
}
2022-07-13 07:07:15 +00:00
Texture * lovrTextureCreate ( const TextureInfo * info ) {
2022-04-30 03:38:34 +00:00
uint32_t limits [ ] = {
[ TEXTURE_2D ] = state . limits . textureSize2D ,
2022-04-30 03:56:23 +00:00
[ TEXTURE_3D ] = state . limits . textureSize3D ,
2022-04-30 03:38:34 +00:00
[ TEXTURE_CUBE ] = state . limits . textureSizeCube ,
2022-04-30 03:56:23 +00:00
[ TEXTURE_ARRAY ] = state . limits . textureSize2D
2022-04-30 03:38:34 +00:00
} ;
uint32_t limit = limits [ info - > type ] ;
2022-07-30 22:08:30 +00:00
uint32_t mipmapCap = log2 ( MAX ( MAX ( info - > width , info - > height ) , ( info - > type = = TEXTURE_3D ? info - > layers : 1 ) ) ) + 1 ;
2022-05-01 01:49:46 +00:00
uint32_t mipmaps = CLAMP ( info - > mipmaps , 1 , mipmapCap ) ;
2022-04-30 03:38:34 +00:00
uint8_t supports = state . features . formats [ info - > format ] ;
lovrCheck ( info - > width > 0 , " Texture width must be greater than zero " ) ;
lovrCheck ( info - > height > 0 , " Texture height must be greater than zero " ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( info - > layers > 0 , " Texture layer count must be greater than zero " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > width < = limit , " Texture %s exceeds the limit for this texture type (%d) " , " width " , limit ) ;
lovrCheck ( info - > height < = limit , " Texture %s exceeds the limit for this texture type (%d) " , " height " , limit ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( info - > layers < = limit | | info - > type ! = TEXTURE_3D , " Texture %s exceeds the limit for this texture type (%d) " , " layer count " , limit ) ;
lovrCheck ( info - > layers < = state . limits . textureLayers | | info - > type ! = TEXTURE_ARRAY , " Texture %s exceeds the limit for this texture type (%d) " , " layer count " , limit ) ;
lovrCheck ( info - > layers = = 1 | | info - > type ! = TEXTURE_2D , " 2D textures must have a layer count of 1 " ) ;
lovrCheck ( info - > layers = = 6 | | info - > type ! = TEXTURE_CUBE , " Cubemaps must have a layer count of 6 " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > width = = info - > height | | info - > type ! = TEXTURE_CUBE , " Cubemaps must be square " ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( measureTexture ( info - > format , info - > width , info - > height , info - > layers ) < 1 < < 30 , " Memory for a Texture can not exceed 1GB " ) ; // TODO mip?
2022-05-26 06:52:24 +00:00
lovrCheck ( info - > samples = = 1 | | info - > samples = = 4 , " Texture multisample count must be 1 or 4...for now " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > samples = = 1 | | info - > type ! = TEXTURE_CUBE , " Cubemaps can not be multisampled " ) ;
2022-04-30 03:56:23 +00:00
lovrCheck ( info - > samples = = 1 | | info - > type ! = TEXTURE_3D , " Volume textures can not be multisampled " ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( info - > samples = = 1 | | ~ info - > usage & TEXTURE_STORAGE , " Textures with the 'storage' flag can not be multisampled...for now " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( info - > samples = = 1 | | mipmaps = = 1 , " Multisampled textures can only have 1 mipmap " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( ~ info - > usage & TEXTURE_SAMPLE | | ( supports & GPU_FEATURE_SAMPLE ) , " GPU does not support the 'sample' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | ( supports & GPU_FEATURE_RENDER ) , " GPU does not support the 'render' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_STORAGE | | ( supports & GPU_FEATURE_STORAGE ) , " GPU does not support the 'storage' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | info - > width < = state . limits . renderSize [ 0 ] , " Texture has 'render' flag but its size exceeds the renderSize limit " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | info - > height < = state . limits . renderSize [ 1 ] , " Texture has 'render' flag but its size exceeds the renderSize limit " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( mipmaps < = mipmapCap , " Texture has more than the max number of mipmap levels for its size (%d) " , mipmapCap ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( ( info - > format < FORMAT_BC1 | | info - > format > FORMAT_BC7 ) | | state . features . textureBC , " %s textures are not supported on this GPU " , " BC " ) ;
lovrCheck ( info - > format < FORMAT_ASTC_4x4 | | state . features . textureASTC , " %s textures are not supported on this GPU " , " ASTC " ) ;
Texture * texture = calloc ( 1 , sizeof ( Texture ) + gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
texture - > gpu = ( gpu_texture * ) ( texture + 1 ) ;
texture - > info = * info ;
2022-05-01 01:49:46 +00:00
texture - > info . mipmaps = mipmaps ;
uint32_t levelCount = 0 ;
uint32_t levelOffsets [ 16 ] ;
uint32_t levelSizes [ 16 ] ;
2022-05-01 22:18:56 +00:00
gpu_buffer * scratchpad = NULL ;
2022-05-01 01:49:46 +00:00
if ( info - > imageCount > 0 ) {
levelCount = lovrImageGetLevelCount ( info - > images [ 0 ] ) ;
lovrCheck ( info - > type ! = TEXTURE_3D | | levelCount = = 1 , " Images used to initialize 3D textures can not have mipmaps " ) ;
uint32_t total = 0 ;
for ( uint32_t level = 0 ; level < levelCount ; level + + ) {
levelOffsets [ level ] = total ;
uint32_t width = MAX ( info - > width > > level , 1 ) ;
uint32_t height = MAX ( info - > height > > level , 1 ) ;
2022-07-30 22:08:30 +00:00
levelSizes [ level ] = measureTexture ( info - > format , width , height , info - > layers ) ;
2022-05-01 01:49:46 +00:00
total + = levelSizes [ level ] ;
}
scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
char * data = gpu_map ( scratchpad , total , 64 , GPU_MAP_WRITE ) ;
for ( uint32_t level = 0 ; level < levelCount ; level + + ) {
2022-07-30 22:08:30 +00:00
for ( uint32_t layer = 0 ; layer < info - > layers ; layer + + ) {
2022-05-01 01:49:46 +00:00
Image * image = info - > imageCount = = 1 ? info - > images [ 0 ] : info - > images [ layer ] ;
uint32_t slice = info - > imageCount = = 1 ? layer : 0 ;
2022-08-07 01:05:30 +00:00
size_t size = lovrImageGetLayerSize ( image , level ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( size = = levelSizes [ level ] / info - > layers , " Texture/Image size mismatch! " ) ;
2022-05-01 22:18:56 +00:00
void * pixels = lovrImageGetLayerData ( image , level , slice ) ;
2022-05-01 01:49:46 +00:00
memcpy ( data , pixels , size ) ;
data + = size ;
}
}
}
2022-04-30 03:38:34 +00:00
2022-06-12 05:55:43 +00:00
beginFrame ( ) ;
2022-04-30 03:38:34 +00:00
gpu_texture_init ( texture - > gpu , & ( gpu_texture_info ) {
. type = ( gpu_texture_type ) info - > type ,
. format = ( gpu_texture_format ) info - > format ,
2022-07-30 22:08:30 +00:00
. size = { info - > width , info - > height , info - > layers } ,
2022-04-30 03:38:34 +00:00
. mipmaps = texture - > info . mipmaps ,
. samples = MAX ( info - > samples , 1 ) ,
2022-08-03 05:00:11 +00:00
. usage = convertTextureUsage ( info - > usage ) ,
2022-04-30 03:38:34 +00:00
. srgb = info - > srgb ,
. handle = info - > handle ,
2022-05-01 01:49:46 +00:00
. label = info - > label ,
. upload = {
2022-06-12 05:55:43 +00:00
. stream = state . stream ,
2022-05-01 01:49:46 +00:00
. buffer = scratchpad ,
. levelCount = levelCount ,
. levelOffsets = levelOffsets ,
. generateMipmaps = levelCount < mipmaps
}
2022-04-30 03:38:34 +00:00
} ) ;
// Automatically create a renderable view for renderable non-volume textures
2022-07-30 22:08:30 +00:00
if ( ( info - > usage & TEXTURE_RENDER ) & & info - > type ! = TEXTURE_3D & & info - > layers < = state . limits . renderSize [ 2 ] ) {
2022-04-30 03:38:34 +00:00
if ( info - > mipmaps = = 1 ) {
texture - > renderView = texture - > gpu ;
} else {
gpu_texture_view_info view = {
. source = texture - > gpu ,
. type = GPU_TEXTURE_ARRAY ,
2022-07-30 22:08:30 +00:00
. layerCount = info - > layers ,
2022-04-30 03:38:34 +00:00
. levelCount = 1
} ;
texture - > renderView = malloc ( gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture - > renderView , " Out of memory " ) ;
lovrAssert ( gpu_texture_init_view ( texture - > renderView , & view ) , " Failed to create texture view " ) ;
}
}
2022-06-12 02:07:46 +00:00
// Sample-only textures are exempt from sync tracking to reduce overhead. Instead, they are
// manually synchronized with a single barrier after the upload stream.
if ( info - > usage = = TEXTURE_SAMPLE ) {
state . hasTextureUpload = true ;
} else if ( levelCount > 0 ) {
texture - > sync . writePhase = GPU_PHASE_TRANSFER ;
2022-06-12 05:55:43 +00:00
texture - > sync . pendingWrite = GPU_CACHE_TRANSFER_WRITE ;
2022-06-12 02:07:46 +00:00
}
2022-04-30 03:38:34 +00:00
return texture ;
}
2022-07-13 07:07:15 +00:00
Texture * lovrTextureCreateView ( const TextureViewInfo * view ) {
2022-04-30 03:56:23 +00:00
const TextureInfo * info = & view - > parent - > info ;
2022-07-30 22:08:30 +00:00
uint32_t maxLayers = info - > type = = TEXTURE_3D ? MAX ( info - > layers > > view - > levelIndex , 1 ) : info - > layers ;
2022-04-30 03:56:23 +00:00
lovrCheck ( ! info - > parent , " Can't nest texture views " ) ;
lovrCheck ( view - > type ! = TEXTURE_3D , " Texture views may not be volume textures " ) ;
lovrCheck ( view - > layerCount > 0 , " Texture view must have at least one layer " ) ;
lovrCheck ( view - > levelCount > 0 , " Texture view must have at least one mipmap " ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( view - > layerIndex + view - > layerCount < = maxLayers , " Texture view layer range exceeds layer count of parent texture " ) ;
2022-04-30 03:56:23 +00:00
lovrCheck ( view - > levelIndex + view - > levelCount < = info - > mipmaps , " Texture view mipmap range exceeds mipmap count of parent texture " ) ;
lovrCheck ( view - > layerCount = = 1 | | view - > type ! = TEXTURE_2D , " 2D texture can only have a single layer " ) ;
lovrCheck ( view - > levelCount = = 1 | | info - > type ! = TEXTURE_3D , " Views of volume textures may only have a single mipmap level " ) ;
lovrCheck ( view - > layerCount = = 6 | | view - > type ! = TEXTURE_CUBE , " Cubemaps can only have a six layers " ) ;
Texture * texture = calloc ( 1 , sizeof ( Texture ) + gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
texture - > gpu = ( gpu_texture * ) ( texture + 1 ) ;
texture - > info = * info ;
texture - > info . parent = view - > parent ;
texture - > info . mipmaps = view - > levelCount ;
texture - > info . width = MAX ( info - > width > > view - > levelIndex , 1 ) ;
texture - > info . height = MAX ( info - > height > > view - > levelIndex , 1 ) ;
2022-07-30 22:08:30 +00:00
texture - > info . layers = view - > layerCount ;
2022-04-30 03:56:23 +00:00
gpu_texture_init_view ( texture - > gpu , & ( gpu_texture_view_info ) {
. source = view - > parent - > gpu ,
. type = ( gpu_texture_type ) view - > type ,
. layerIndex = view - > layerIndex ,
. layerCount = view - > layerCount ,
. levelIndex = view - > levelIndex ,
. levelCount = view - > levelCount
} ) ;
if ( view - > levelCount = = 1 & & view - > type ! = TEXTURE_3D & & view - > layerCount < = 6 ) {
texture - > renderView = texture - > gpu ;
}
lovrRetain ( view - > parent ) ;
return texture ;
}
2022-04-30 03:38:34 +00:00
void lovrTextureDestroy ( void * ref ) {
Texture * texture = ref ;
2022-05-11 22:28:04 +00:00
if ( texture ! = state . window ) {
2022-06-25 02:38:45 +00:00
lovrRelease ( texture - > material , lovrMaterialDestroy ) ;
2022-05-11 22:28:04 +00:00
lovrRelease ( texture - > info . parent , lovrTextureDestroy ) ;
if ( texture - > renderView & & texture - > renderView ! = texture - > gpu ) gpu_texture_destroy ( texture - > renderView ) ;
if ( texture - > gpu ) gpu_texture_destroy ( texture - > gpu ) ;
}
2022-04-30 03:38:34 +00:00
free ( texture ) ;
}
2022-04-30 03:56:23 +00:00
const TextureInfo * lovrTextureGetInfo ( Texture * texture ) {
return & texture - > info ;
}
2022-06-25 02:38:45 +00:00
static Material * lovrTextureGetMaterial ( Texture * texture ) {
if ( ! texture - > material ) {
texture - > material = lovrMaterialCreate ( & ( MaterialInfo ) {
. data . color = { 1.f , 1.f , 1.f , 1.f } ,
. data . uvScale = { 1.f , 1.f } ,
. texture = texture
} ) ;
// Since the Material refcounts the Texture, this creates a cycle. Release the texture to make
// sure this is a weak relationship (the automaterial does not keep the texture refcounted).
lovrRelease ( texture , lovrTextureDestroy ) ;
}
return texture - > material ;
}
2022-05-01 22:47:17 +00:00
// Sampler
2022-06-08 03:42:10 +00:00
Sampler * lovrGraphicsGetDefaultSampler ( FilterMode mode ) {
return state . defaultSamplers [ mode ] ;
}
2022-07-13 07:07:15 +00:00
Sampler * lovrSamplerCreate ( const SamplerInfo * info ) {
2022-05-01 22:47:17 +00:00
lovrCheck ( info - > range [ 1 ] < 0.f | | info - > range [ 1 ] > = info - > range [ 0 ] , " Invalid Sampler mipmap range " ) ;
lovrCheck ( info - > anisotropy < = state . limits . anisotropy , " Sampler anisotropy (%f) exceeds anisotropy limit (%f) " , info - > anisotropy , state . limits . anisotropy ) ;
Sampler * sampler = calloc ( 1 , sizeof ( Sampler ) + gpu_sizeof_sampler ( ) ) ;
lovrAssert ( sampler , " Out of memory " ) ;
2022-05-09 18:47:06 +00:00
sampler - > ref = 1 ;
2022-05-01 22:47:17 +00:00
sampler - > gpu = ( gpu_sampler * ) ( sampler + 1 ) ;
sampler - > info = * info ;
gpu_sampler_info gpu = {
. min = ( gpu_filter ) info - > min ,
. mag = ( gpu_filter ) info - > mag ,
. mip = ( gpu_filter ) info - > mip ,
. wrap [ 0 ] = ( gpu_wrap ) info - > wrap [ 0 ] ,
. wrap [ 1 ] = ( gpu_wrap ) info - > wrap [ 1 ] ,
. wrap [ 2 ] = ( gpu_wrap ) info - > wrap [ 2 ] ,
. compare = ( gpu_compare_mode ) info - > compare ,
. anisotropy = MIN ( info - > anisotropy , state . limits . anisotropy ) ,
. lodClamp = { info - > range [ 0 ] , info - > range [ 1 ] }
} ;
2022-05-09 18:47:06 +00:00
gpu_sampler_init ( sampler - > gpu , & gpu ) ;
2022-05-01 22:47:17 +00:00
return sampler ;
}
void lovrSamplerDestroy ( void * ref ) {
Sampler * sampler = ref ;
gpu_sampler_destroy ( sampler - > gpu ) ;
free ( sampler ) ;
}
const SamplerInfo * lovrSamplerGetInfo ( Sampler * sampler ) {
return & sampler - > info ;
}
2022-05-09 18:47:06 +00:00
// Shader
2022-07-10 06:09:02 +00:00
ShaderSource lovrGraphicsCompileShader ( ShaderStage stage , ShaderSource * source ) {
uint32_t magic = 0x07230203 ;
2022-05-22 21:58:07 +00:00
2022-07-10 06:09:02 +00:00
if ( source - > size % 4 = = 0 & & source - > size > = 4 & & ! memcmp ( source - > code , & magic , 4 ) ) {
return * source ;
2022-05-22 21:58:07 +00:00
}
2022-05-09 18:47:06 +00:00
# ifdef LOVR_USE_GLSLANG
const glslang_stage_t stages [ ] = {
[ STAGE_VERTEX ] = GLSLANG_STAGE_VERTEX ,
[ STAGE_FRAGMENT ] = GLSLANG_STAGE_FRAGMENT ,
[ STAGE_COMPUTE ] = GLSLANG_STAGE_COMPUTE
} ;
2022-07-07 05:54:56 +00:00
const char * stageNames [ ] = {
[ STAGE_VERTEX ] = " vertex " ,
[ STAGE_FRAGMENT ] = " fragment " ,
[ STAGE_COMPUTE ] = " compute "
} ;
2022-06-16 03:46:43 +00:00
const char * prefix = " "
" #version 460 \n "
" #extension GL_EXT_multiview : require \n "
" #extension GL_GOOGLE_include_directive : require \n " ;
2022-06-10 00:44:20 +00:00
const char * strings [ ] = {
2022-06-16 03:46:43 +00:00
prefix ,
( const char * ) etc_shaders_lovr_glsl ,
2022-07-10 04:39:31 +00:00
" #line 1 \n " ,
2022-07-31 18:18:15 +00:00
source - > code
2022-06-10 00:44:20 +00:00
} ;
2022-08-07 01:05:30 +00:00
lovrCheck ( source - > size < = INT_MAX , " Shader is way too big " ) ;
2022-06-10 00:44:20 +00:00
int lengths [ ] = {
2022-06-16 03:46:43 +00:00
- 1 ,
etc_shaders_lovr_glsl_len ,
2022-07-10 04:39:31 +00:00
- 1 ,
2022-08-07 01:05:30 +00:00
( int ) source - > size
2022-06-10 00:44:20 +00:00
} ;
2022-06-16 03:46:43 +00:00
const glslang_resource_t * resource = glslang_default_resource ( ) ;
2022-05-09 18:47:06 +00:00
glslang_input_t input = {
. language = GLSLANG_SOURCE_GLSL ,
. stage = stages [ stage ] ,
. client = GLSLANG_CLIENT_VULKAN ,
. client_version = GLSLANG_TARGET_VULKAN_1_1 ,
. target_language = GLSLANG_TARGET_SPV ,
. target_language_version = GLSLANG_TARGET_SPV_1_3 ,
2022-06-10 00:44:20 +00:00
. strings = strings ,
. lengths = lengths ,
2022-06-16 03:46:43 +00:00
. string_count = COUNTOF ( strings ) ,
2022-05-09 18:47:06 +00:00
. default_version = 460 ,
. default_profile = GLSLANG_NO_PROFILE ,
. resource = resource
} ;
glslang_shader_t * shader = glslang_shader_create ( & input ) ;
2022-08-03 05:05:44 +00:00
int options = 0 ;
options | = GLSLANG_SHADER_AUTO_MAP_BINDINGS ;
options | = GLSLANG_SHADER_AUTO_MAP_LOCATIONS ;
glslang_shader_set_options ( shader , options ) ;
2022-05-09 18:47:06 +00:00
if ( ! glslang_shader_preprocess ( shader , & input ) ) {
2022-07-10 04:39:31 +00:00
lovrThrow ( " Could not preprocess %s shader: \n %s " , stageNames [ stage ] , glslang_shader_get_info_log ( shader ) ) ;
2022-07-10 06:09:02 +00:00
return ( ShaderSource ) { NULL , 0 } ;
2022-05-09 18:47:06 +00:00
}
if ( ! glslang_shader_parse ( shader , & input ) ) {
2022-07-10 04:39:31 +00:00
lovrThrow ( " Could not parse %s shader: \n %s " , stageNames [ stage ] , glslang_shader_get_info_log ( shader ) ) ;
2022-07-10 06:09:02 +00:00
return ( ShaderSource ) { NULL , 0 } ;
2022-05-09 18:47:06 +00:00
}
glslang_program_t * program = glslang_program_create ( ) ;
glslang_program_add_shader ( program , shader ) ;
if ( ! glslang_program_link ( program , 0 ) ) {
2022-07-10 04:39:31 +00:00
lovrThrow ( " Could not link shader: \n %s " , glslang_program_get_info_log ( program ) ) ;
2022-07-10 06:09:02 +00:00
return ( ShaderSource ) { NULL , 0 } ;
2022-05-09 18:47:06 +00:00
}
glslang_program_SPIRV_generate ( program , stages [ stage ] ) ;
void * words = glslang_program_SPIRV_get_ptr ( program ) ;
size_t size = glslang_program_SPIRV_get_size ( program ) * 4 ;
2022-07-10 06:09:02 +00:00
2022-05-09 18:47:06 +00:00
void * data = malloc ( size ) ;
lovrAssert ( data , " Out of memory " ) ;
memcpy ( data , words , size ) ;
glslang_program_delete ( program ) ;
glslang_shader_delete ( shader ) ;
2022-07-10 06:09:02 +00:00
return ( ShaderSource ) { data , size } ;
2022-05-22 22:09:09 +00:00
# else
2022-07-10 04:39:31 +00:00
lovrThrow ( " Could not compile shader: No shader compiler available " ) ;
2022-07-10 06:09:02 +00:00
return ( ShaderSource ) { NULL , 0 } ;
2022-05-22 22:09:09 +00:00
# endif
}
static void lovrShaderInit ( Shader * shader ) {
// Shaders store the full list of their flags so clones can override them, but they are reordered
// to put overridden (active) ones first, so a contiguous list can be used to create pipelines
for ( uint32_t i = 0 ; i < shader - > info . flagCount ; i + + ) {
ShaderFlag * flag = & shader - > info . flags [ i ] ;
uint32_t hash = flag - > name ? ( uint32_t ) hash64 ( flag - > name , strlen ( flag - > name ) ) : 0 ;
for ( uint32_t j = 0 ; j < shader - > flagCount ; j + + ) {
if ( hash ? ( hash ! = shader - > flagLookup [ j ] ) : ( flag - > id ! = shader - > flags [ j ] . id ) ) continue ;
uint32_t index = shader - > overrideCount + + ;
if ( index ! = j ) {
gpu_shader_flag temp = shader - > flags [ index ] ;
shader - > flags [ index ] = shader - > flags [ j ] ;
shader - > flags [ j ] = temp ;
uint32_t tempHash = shader - > flagLookup [ index ] ;
shader - > flagLookup [ index ] = shader - > flagLookup [ j ] ;
shader - > flagLookup [ j ] = tempHash ;
}
shader - > flags [ index ] . value = flag - > value ;
}
}
2022-05-24 04:44:42 +00:00
if ( shader - > info . type = = SHADER_COMPUTE ) {
gpu_compute_pipeline_info pipelineInfo = {
. shader = shader - > gpu ,
. flags = shader - > flags ,
. flagCount = shader - > overrideCount
} ;
gpu_pipeline * pipeline = malloc ( gpu_sizeof_pipeline ( ) ) ;
lovrAssert ( pipeline , " Out of memory " ) ;
gpu_pipeline_init_compute ( pipeline , & pipelineInfo ) ;
2022-08-07 01:05:30 +00:00
shader - > computePipelineIndex = state . pipelines . length ;
2022-05-24 04:44:42 +00:00
arr_push ( & state . pipelines , pipeline ) ;
}
2022-05-09 18:47:06 +00:00
}
2022-08-02 05:10:06 +00:00
ShaderSource lovrGraphicsGetDefaultShaderSource ( DefaultShader type , ShaderStage stage ) {
if ( stage = = STAGE_COMPUTE ) {
return ( ShaderSource ) { NULL , 0 } ;
}
const ShaderSource sources [ ] [ 2 ] = {
[ SHADER_UNLIT ] = {
{ lovr_shader_unlit_vert , sizeof ( lovr_shader_unlit_vert ) } ,
{ lovr_shader_unlit_frag , sizeof ( lovr_shader_unlit_frag ) }
} ,
[ SHADER_FONT ] = {
{ lovr_shader_unlit_vert , sizeof ( lovr_shader_unlit_vert ) } ,
{ lovr_shader_font_frag , sizeof ( lovr_shader_font_frag ) }
} ,
[ SHADER_CUBEMAP ] = {
{ lovr_shader_cubemap_vert , sizeof ( lovr_shader_cubemap_vert ) } ,
{ lovr_shader_cubemap_frag , sizeof ( lovr_shader_cubemap_frag ) }
} ,
[ SHADER_EQUIRECT ] = {
{ lovr_shader_cubemap_vert , sizeof ( lovr_shader_cubemap_vert ) } ,
{ lovr_shader_equirect_frag , sizeof ( lovr_shader_equirect_frag ) }
} ,
[ SHADER_FILL ] = {
{ lovr_shader_fill_vert , sizeof ( lovr_shader_fill_vert ) } ,
{ lovr_shader_unlit_frag , sizeof ( lovr_shader_unlit_frag ) }
} ,
[ SHADER_STEREOBLIT ] = {
{ lovr_shader_fill_vert , sizeof ( lovr_shader_fill_vert ) } ,
{ lovr_shader_stereoblit_frag , sizeof ( lovr_shader_stereoblit_frag ) }
}
} ;
return sources [ type ] [ stage ] ;
}
2022-05-28 03:47:07 +00:00
Shader * lovrGraphicsGetDefaultShader ( DefaultShader type ) {
if ( state . defaultShaders [ type ] ) {
return state . defaultShaders [ type ] ;
}
2022-08-02 05:10:06 +00:00
ShaderInfo info = {
. type = SHADER_GRAPHICS ,
. source [ 0 ] = lovrGraphicsGetDefaultShaderSource ( type , STAGE_VERTEX ) ,
. source [ 1 ] = lovrGraphicsGetDefaultShaderSource ( type , STAGE_FRAGMENT )
} ;
2022-05-28 03:47:07 +00:00
2022-07-10 06:09:02 +00:00
return state . defaultShaders [ type ] = lovrShaderCreate ( & info ) ;
2022-05-28 03:47:07 +00:00
}
2022-07-13 07:07:15 +00:00
Shader * lovrShaderCreate ( const ShaderInfo * info ) {
2022-05-09 18:47:06 +00:00
Shader * shader = calloc ( 1 , sizeof ( Shader ) + gpu_sizeof_shader ( ) ) ;
lovrAssert ( shader , " Out of memory " ) ;
2022-05-22 22:09:09 +00:00
uint32_t stageCount = info - > type = = SHADER_GRAPHICS ? 2 : 1 ;
uint32_t firstStage = info - > type = = SHADER_GRAPHICS ? GPU_STAGE_VERTEX : GPU_STAGE_COMPUTE ;
uint32_t userSet = info - > type = = SHADER_GRAPHICS ? 2 : 0 ;
spv_result result ;
spv_info spv [ 2 ] = { 0 } ;
for ( uint32_t i = 0 ; i < stageCount ; i + + ) {
2022-07-10 06:09:02 +00:00
result = spv_parse ( info - > source [ i ] . code , info - > source [ i ] . size , & spv [ i ] ) ;
2022-05-22 22:09:09 +00:00
lovrCheck ( result = = SPV_OK , " Failed to load Shader: %s \n " , spv_result_to_string ( result ) ) ;
2022-06-01 03:47:47 +00:00
lovrCheck ( spv [ i ] . version < = 0x00010300 , " Invalid SPIR-V version (up to 1.3 is supported) " ) ;
2022-05-22 22:09:09 +00:00
spv [ i ] . features = tempAlloc ( spv [ i ] . featureCount * sizeof ( uint32_t ) ) ;
spv [ i ] . specConstants = tempAlloc ( spv [ i ] . specConstantCount * sizeof ( spv_spec_constant ) ) ;
spv [ i ] . pushConstants = tempAlloc ( spv [ i ] . pushConstantCount * sizeof ( spv_push_constant ) ) ;
2022-06-16 03:46:43 +00:00
spv [ i ] . attributes = tempAlloc ( spv [ i ] . attributeCount * sizeof ( spv_attribute ) ) ;
2022-05-22 22:09:09 +00:00
spv [ i ] . resources = tempAlloc ( spv [ i ] . resourceCount * sizeof ( spv_resource ) ) ;
2022-07-10 06:09:02 +00:00
result = spv_parse ( info - > source [ i ] . code , info - > source [ i ] . size , & spv [ i ] ) ;
2022-05-22 22:09:09 +00:00
lovrCheck ( result = = SPV_OK , " Failed to load Shader: %s \n " , spv_result_to_string ( result ) ) ;
checkShaderFeatures ( spv [ i ] . features , spv [ i ] . featureCount ) ;
}
2022-07-10 06:39:03 +00:00
if ( info - > type = = SHADER_COMPUTE ) {
2022-08-06 20:06:42 +00:00
memcpy ( shader - > workgroupSize , spv [ 0 ] . workgroupSize , 3 * sizeof ( uint32_t ) ) ;
lovrCheck ( shader - > workgroupSize [ 0 ] < = state . limits . workgroupSize [ 0 ] , " Shader workgroup size exceeds the 'workgroupSize' limit " ) ;
lovrCheck ( shader - > workgroupSize [ 1 ] < = state . limits . workgroupSize [ 1 ] , " Shader workgroup size exceeds the 'workgroupSize' limit " ) ;
lovrCheck ( shader - > workgroupSize [ 2 ] < = state . limits . workgroupSize [ 2 ] , " Shader workgroup size exceeds the 'workgroupSize' limit " ) ;
uint32_t totalWorkgroupSize = shader - > workgroupSize [ 0 ] * shader - > workgroupSize [ 1 ] * shader - > workgroupSize [ 2 ] ;
lovrCheck ( totalWorkgroupSize < = state . limits . totalWorkgroupSize , " Shader workgroup size exceeds the 'totalWorkgroupSize' limit " ) ;
2022-07-10 06:39:03 +00:00
}
2022-05-22 22:09:09 +00:00
uint32_t constantStage = spv [ 0 ] . pushConstantSize > spv [ 1 ] . pushConstantSize ? 0 : 1 ;
uint32_t maxFlags = spv [ 0 ] . specConstantCount + spv [ 1 ] . specConstantCount ;
2022-06-16 03:46:43 +00:00
shader - > constantCount = spv [ constantStage ] . pushConstantCount ;
shader - > attributeCount = spv [ 0 ] . attributeCount ;
2022-05-22 22:09:09 +00:00
shader - > constantSize = MAX ( spv [ 0 ] . pushConstantSize , spv [ 1 ] . pushConstantSize ) ;
shader - > constants = malloc ( spv [ constantStage ] . pushConstantCount * sizeof ( ShaderConstant ) ) ;
shader - > resources = malloc ( ( spv [ 0 ] . resourceCount + spv [ 1 ] . resourceCount ) * sizeof ( ShaderResource ) ) ;
2022-06-16 03:46:43 +00:00
shader - > attributes = malloc ( spv [ 0 ] . attributeCount * sizeof ( ShaderAttribute ) ) ;
2022-05-22 22:09:09 +00:00
gpu_slot * slots = tempAlloc ( ( spv [ 0 ] . resourceCount + spv [ 1 ] . resourceCount ) * sizeof ( gpu_slot ) ) ;
shader - > flags = malloc ( maxFlags * sizeof ( gpu_shader_flag ) ) ;
shader - > flagLookup = malloc ( maxFlags * sizeof ( uint32_t ) ) ;
2022-06-16 03:46:43 +00:00
lovrAssert ( shader - > constants & & shader - > resources & & shader - > attributes , " Out of memory " ) ;
lovrAssert ( shader - > flags & & shader - > flagLookup , " Out of memory " ) ;
2022-05-22 22:09:09 +00:00
// Push constants
for ( uint32_t i = 0 ; i < spv [ constantStage ] . pushConstantCount ; i + + ) {
static const FieldType constantTypes [ ] = {
[ SPV_B32 ] = FIELD_U32 ,
[ SPV_I32 ] = FIELD_I32 ,
[ SPV_I32x2 ] = FIELD_I32x2 ,
[ SPV_I32x3 ] = FIELD_I32x3 ,
[ SPV_I32x4 ] = FIELD_I32x4 ,
[ SPV_U32 ] = FIELD_U32 ,
[ SPV_U32x2 ] = FIELD_U32x2 ,
[ SPV_U32x3 ] = FIELD_U32x3 ,
[ SPV_U32x4 ] = FIELD_U32x4 ,
[ SPV_F32 ] = FIELD_F32 ,
[ SPV_F32x2 ] = FIELD_F32x2 ,
[ SPV_F32x3 ] = FIELD_F32x3 ,
[ SPV_F32x4 ] = FIELD_F32x4 ,
[ SPV_MAT2 ] = FIELD_MAT2 ,
[ SPV_MAT3 ] = FIELD_MAT3 ,
[ SPV_MAT4 ] = FIELD_MAT4
} ;
spv_push_constant * constant = & spv [ constantStage ] . pushConstants [ i ] ;
shader - > constants [ i ] = ( ShaderConstant ) {
. hash = ( uint32_t ) hash64 ( constant - > name , strlen ( constant - > name ) ) ,
. offset = constant - > offset ,
. type = constantTypes [ constant - > type ]
} ;
}
// Resources
for ( uint32_t s = 0 ; s < stageCount ; s + + ) {
for ( uint32_t i = 0 ; i < spv [ s ] . resourceCount ; i + + ) {
spv_resource * resource = & spv [ s ] . resources [ i ] ;
if ( resource - > set ! = userSet ) {
continue ;
}
static const gpu_slot_type resourceTypes [ ] = {
[ SPV_UNIFORM_BUFFER ] = GPU_SLOT_UNIFORM_BUFFER ,
[ SPV_STORAGE_BUFFER ] = GPU_SLOT_STORAGE_BUFFER ,
[ SPV_SAMPLED_TEXTURE ] = GPU_SLOT_SAMPLED_TEXTURE ,
[ SPV_STORAGE_TEXTURE ] = GPU_SLOT_STORAGE_TEXTURE ,
[ SPV_SAMPLER ] = GPU_SLOT_SAMPLER
} ;
uint32_t hash = ( uint32_t ) hash64 ( resource - > name , strlen ( resource - > name ) ) ;
uint32_t stage = s = = 0 ? firstStage : GPU_STAGE_FRAGMENT ;
bool append = true ;
if ( s > 0 ) {
for ( uint32_t j = 0 ; j < shader - > resourceCount ; j + + ) {
ShaderResource * other = & shader - > resources [ j ] ;
if ( other - > binding = = resource - > binding ) {
lovrCheck ( other - > type = = resourceTypes [ resource - > type ] , " Shader variable (%d) does not use a consistent type " , resource - > binding ) ;
shader - > resources [ j ] . stageMask | = stage ;
append = false ;
break ;
}
}
}
if ( ! append ) {
continue ;
}
uint32_t index = shader - > resourceCount + + ;
2022-06-04 21:28:23 +00:00
if ( shader - > resourceCount > MAX_SHADER_RESOURCES ) {
lovrThrow ( " Shader resource count exceeds resourcesPerShader limit (%d) " , MAX_SHADER_RESOURCES ) ;
2022-06-01 03:16:16 +00:00
}
2022-06-01 03:24:43 +00:00
lovrCheck ( resource - > binding < 32 , " Max resource binding number is %d " , 32 - 1 ) ;
2022-05-22 22:09:09 +00:00
slots [ index ] = ( gpu_slot ) {
. number = resource - > binding ,
. type = resourceTypes [ resource - > type ] ,
2022-05-24 04:40:57 +00:00
. stages = stage
2022-05-22 22:09:09 +00:00
} ;
shader - > resources [ index ] = ( ShaderResource ) {
. hash = hash ,
. binding = resource - > binding ,
. stageMask = stage ,
. type = resourceTypes [ resource - > type ]
} ;
2022-05-24 05:32:36 +00:00
bool buffer = resource - > type = = SPV_UNIFORM_BUFFER | | resource - > type = = SPV_STORAGE_BUFFER ;
bool texture = resource - > type = = SPV_SAMPLED_TEXTURE | | resource - > type = = SPV_STORAGE_TEXTURE ;
bool sampler = resource - > type = = SPV_SAMPLER ;
bool storage = resource - > type = = SPV_STORAGE_BUFFER | | resource - > type = = SPV_STORAGE_TEXTURE ;
shader - > bufferMask | = ( buffer < < resource - > binding ) ;
shader - > textureMask | = ( texture < < resource - > binding ) ;
shader - > samplerMask | = ( sampler < < resource - > binding ) ;
shader - > storageMask | = ( storage < < resource - > binding ) ;
2022-05-22 22:09:09 +00:00
}
}
2022-06-16 03:46:43 +00:00
// Attributes
for ( uint32_t i = 0 ; i < spv [ 0 ] . attributeCount ; i + + ) {
shader - > attributes [ i ] . location = spv [ 0 ] . attributes [ i ] . location ;
shader - > attributes [ i ] . hash = ( uint32_t ) hash64 ( spv [ 0 ] . attributes [ i ] . name , strlen ( spv [ 0 ] . attributes [ i ] . name ) ) ;
shader - > hasCustomAttributes | = shader - > attributes [ i ] . location < 10 ;
}
2022-05-22 22:09:09 +00:00
// Specialization constants
for ( uint32_t s = 0 ; s < stageCount ; s + + ) {
for ( uint32_t i = 0 ; i < spv [ s ] . specConstantCount ; i + + ) {
spv_spec_constant * constant = & spv [ s ] . specConstants [ i ] ;
bool append = true ;
if ( s > 0 ) {
for ( uint32_t j = 0 ; j < spv [ 0 ] . specConstantCount ; j + + ) {
spv_spec_constant * other = & spv [ 0 ] . specConstants [ j ] ;
if ( other - > id = = constant - > id ) {
lovrCheck ( other - > type = = constant - > type , " Shader flag (%d) does not use a consistent type " , constant - > id ) ;
lovrCheck ( ! strcmp ( constant - > name , other - > name ) , " Shader flag (%d) does not use a consistent name " , constant - > id ) ;
append = false ;
break ;
}
}
}
if ( ! append ) {
break ;
}
static const gpu_flag_type flagTypes [ ] = {
[ SPV_B32 ] = GPU_FLAG_B32 ,
[ SPV_I32 ] = GPU_FLAG_I32 ,
[ SPV_U32 ] = GPU_FLAG_U32 ,
[ SPV_F32 ] = GPU_FLAG_F32
} ;
uint32_t index = shader - > flagCount + + ;
2022-07-04 00:26:31 +00:00
if ( constant - > name ) {
shader - > flagLookup [ index ] = ( uint32_t ) hash64 ( constant - > name , strlen ( constant - > name ) ) ;
} else {
shader - > flagLookup [ index ] = 0 ;
}
2022-05-22 22:09:09 +00:00
shader - > flags [ index ] = ( gpu_shader_flag ) {
. id = constant - > id ,
. type = flagTypes [ constant - > type ]
} ;
}
}
2022-05-09 18:47:06 +00:00
shader - > ref = 1 ;
shader - > gpu = ( gpu_shader * ) ( shader + 1 ) ;
shader - > info = * info ;
2022-05-24 04:40:57 +00:00
shader - > layout = getLayout ( slots , shader - > resourceCount ) ;
2022-05-09 18:47:06 +00:00
gpu_shader_info gpu = {
2022-07-10 06:09:02 +00:00
. stages [ 0 ] = { info - > source [ 0 ] . code , info - > source [ 0 ] . size } ,
2022-05-22 22:09:09 +00:00
. pushConstantSize = shader - > constantSize ,
. label = info - > label
2022-05-09 18:47:06 +00:00
} ;
2022-07-10 06:09:02 +00:00
if ( info - > source [ 1 ] . code ) {
gpu . stages [ 1 ] = ( gpu_shader_stage ) { info - > source [ 1 ] . code , info - > source [ 1 ] . size } ;
2022-07-04 00:26:31 +00:00
}
2022-05-24 04:40:57 +00:00
if ( info - > type = = SHADER_GRAPHICS ) {
gpu . layouts [ 0 ] = state . layouts . data [ state . builtinLayout ] . gpu ;
2022-06-17 06:49:09 +00:00
gpu . layouts [ 1 ] = state . layouts . data [ state . materialLayout ] . gpu ;
2022-05-24 04:40:57 +00:00
}
gpu . layouts [ userSet ] = shader - > resourceCount > 0 ? state . layouts . data [ shader - > layout ] . gpu : NULL ;
2022-05-09 18:47:06 +00:00
gpu_shader_init ( shader - > gpu , & gpu ) ;
2022-05-22 22:09:09 +00:00
lovrShaderInit ( shader ) ;
2022-05-22 22:10:07 +00:00
return shader ;
}
2022-05-09 18:47:06 +00:00
2022-05-22 22:10:07 +00:00
Shader * lovrShaderClone ( Shader * parent , ShaderFlag * flags , uint32_t count ) {
Shader * shader = calloc ( 1 , sizeof ( Shader ) + gpu_sizeof_shader ( ) ) ;
lovrAssert ( shader , " Out of memory " ) ;
shader - > ref = 1 ;
lovrRetain ( parent ) ;
shader - > parent = parent ;
shader - > gpu = parent - > gpu ;
shader - > info = parent - > info ;
shader - > info . flags = flags ;
shader - > info . flagCount = count ;
shader - > layout = parent - > layout ;
2022-05-24 05:32:36 +00:00
shader - > bufferMask = parent - > bufferMask ;
shader - > textureMask = parent - > textureMask ;
shader - > samplerMask = parent - > samplerMask ;
shader - > storageMask = parent - > storageMask ;
2022-05-22 22:10:07 +00:00
shader - > constantSize = parent - > constantSize ;
shader - > constantCount = parent - > constantCount ;
shader - > resourceCount = parent - > resourceCount ;
shader - > flagCount = parent - > flagCount ;
shader - > constants = parent - > constants ;
shader - > resources = parent - > resources ;
shader - > flags = malloc ( shader - > flagCount * sizeof ( gpu_shader_flag ) ) ;
shader - > flagLookup = malloc ( shader - > flagCount * sizeof ( uint32_t ) ) ;
lovrAssert ( shader - > flags & & shader - > flagLookup , " Out of memory " ) ;
memcpy ( shader - > flags , parent - > flags , shader - > flagCount * sizeof ( gpu_shader_flag ) ) ;
memcpy ( shader - > flagLookup , parent - > flagLookup , shader - > flagCount * sizeof ( uint32_t ) ) ;
lovrShaderInit ( shader ) ;
2022-05-09 18:47:06 +00:00
return shader ;
}
void lovrShaderDestroy ( void * ref ) {
Shader * shader = ref ;
gpu_shader_destroy ( shader - > gpu ) ;
2022-05-22 22:09:09 +00:00
lovrRelease ( shader - > parent , lovrShaderDestroy ) ;
2022-06-30 07:29:52 +00:00
free ( shader - > constants ) ;
free ( shader - > resources ) ;
free ( shader - > attributes ) ;
free ( shader - > flags ) ;
free ( shader - > flagLookup ) ;
2022-05-09 18:47:06 +00:00
free ( shader ) ;
}
const ShaderInfo * lovrShaderGetInfo ( Shader * shader ) {
return & shader - > info ;
}
2022-07-04 05:59:49 +00:00
bool lovrShaderHasStage ( Shader * shader , ShaderStage stage ) {
switch ( stage ) {
case STAGE_VERTEX : return shader - > info . type = = SHADER_GRAPHICS ;
case STAGE_FRAGMENT : return shader - > info . type = = SHADER_GRAPHICS ;
case STAGE_COMPUTE : return shader - > info . type = = SHADER_COMPUTE ;
default : return false ;
}
}
2022-07-04 06:04:56 +00:00
bool lovrShaderHasAttribute ( Shader * shader , const char * name , uint32_t location ) {
uint32_t hash = name ? ( uint32_t ) hash64 ( name , strlen ( name ) ) : 0 ;
for ( uint32_t i = 0 ; i < shader - > attributeCount ; i + + ) {
ShaderAttribute * attribute = & shader - > attributes [ i ] ;
if ( name ? ( attribute - > hash = = hash ) : ( attribute - > location = = location ) ) {
return true ;
}
}
return false ;
}
2022-08-06 20:06:42 +00:00
void lovrShaderGetWorkgroupSize ( Shader * shader , uint32_t size [ 3 ] ) {
memcpy ( size , shader - > workgroupSize , 3 * sizeof ( uint32_t ) ) ;
2022-07-10 06:39:03 +00:00
}
2022-06-17 06:49:09 +00:00
// Material
2022-07-13 07:07:15 +00:00
Material * lovrMaterialCreate ( const MaterialInfo * info ) {
2022-06-18 00:43:26 +00:00
MaterialBlock * block = & state . materialBlocks . data [ state . materialBlock ] ;
2022-06-17 06:49:09 +00:00
2022-07-14 07:05:58 +00:00
if ( ! block | | block - > head = = ~ 0u | | ! gpu_is_complete ( block - > list [ block - > head ] . tick ) ) {
2022-06-17 06:49:09 +00:00
bool found = false ;
for ( size_t i = 0 ; i < state . materialBlocks . length ; i + + ) {
block = & state . materialBlocks . data [ i ] ;
2022-07-14 07:05:58 +00:00
if ( block - > head ! = ~ 0u & & gpu_is_complete ( block - > list [ block - > head ] . tick ) ) {
2022-06-18 00:43:26 +00:00
state . materialBlock = i ;
2022-06-17 06:49:09 +00:00
found = true ;
break ;
}
}
if ( ! found ) {
arr_expand ( & state . materialBlocks , 1 ) ;
2022-08-07 01:05:30 +00:00
lovrAssert ( state . materialBlocks . length < UINT16_MAX , " Out of memory " ) ;
2022-06-18 00:43:26 +00:00
state . materialBlock = state . materialBlocks . length + + ;
block = & state . materialBlocks . data [ state . materialBlock ] ;
2022-06-17 06:49:09 +00:00
block - > list = malloc ( MATERIALS_PER_BLOCK * sizeof ( Material ) ) ;
block - > buffer = malloc ( gpu_sizeof_buffer ( ) ) ;
block - > bundlePool = malloc ( gpu_sizeof_bundle_pool ( ) ) ;
block - > bundles = malloc ( MATERIALS_PER_BLOCK * gpu_sizeof_bundle ( ) ) ;
lovrAssert ( block - > list & & block - > buffer & & block - > bundlePool & & block - > bundles , " Out of memory " ) ;
for ( uint32_t i = 0 ; i < MATERIALS_PER_BLOCK ; i + + ) {
block - > list [ i ] . next = i + 1 ;
block - > list [ i ] . tick = state . tick - 4 ;
2022-08-07 01:05:30 +00:00
block - > list [ i ] . block = ( uint16_t ) state . materialBlock ;
2022-06-17 06:49:09 +00:00
block - > list [ i ] . index = i ;
block - > list [ i ] . bundle = ( gpu_bundle * ) ( ( char * ) block - > bundles + i * gpu_sizeof_bundle ( ) ) ;
}
block - > list [ MATERIALS_PER_BLOCK - 1 ] . next = ~ 0u ;
block - > tail = MATERIALS_PER_BLOCK - 1 ;
block - > head = 0 ;
gpu_buffer_init ( block - > buffer , & ( gpu_buffer_info ) {
. size = MATERIALS_PER_BLOCK * ALIGN ( sizeof ( MaterialData ) , state . limits . uniformBufferAlign ) ,
. pointer = & block - > pointer ,
. label = " Material Block "
} ) ;
gpu_bundle_pool_info poolInfo = {
. bundles = block - > bundles ,
. layout = state . layouts . data [ state . materialLayout ] . gpu ,
. count = MATERIALS_PER_BLOCK
} ;
gpu_bundle_pool_init ( block - > bundlePool , & poolInfo ) ;
}
}
Material * material = & block - > list [ block - > head ] ;
block - > head = material - > next ;
material - > next = ~ 0u ;
material - > ref = 1 ;
2022-06-18 00:43:26 +00:00
material - > info = * info ;
2022-06-17 06:49:09 +00:00
MaterialData * data ;
uint32_t stride = ALIGN ( sizeof ( MaterialData ) , state . limits . uniformBufferAlign ) ;
if ( block - > pointer ) {
data = ( MaterialData * ) ( ( char * ) block - > pointer + material - > index * stride ) ;
} else {
beginFrame ( ) ;
uint32_t size = stride * MATERIALS_PER_BLOCK ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
data = gpu_map ( scratchpad , size , 4 , GPU_MAP_WRITE ) ;
gpu_copy_buffers ( state . stream , scratchpad , block - > buffer , 0 , stride * material - > index , stride ) ;
state . hasMaterialUpload = true ;
}
memcpy ( data , info , sizeof ( MaterialData ) ) ;
gpu_buffer_binding buffer = {
. object = block - > buffer ,
. offset = material - > index * stride ,
. extent = stride
} ;
gpu_binding bindings [ 8 ] = {
{ 0 , GPU_SLOT_UNIFORM_BUFFER , . buffer = buffer }
} ;
Texture * textures [ ] = {
info - > texture ,
info - > glowTexture ,
info - > occlusionTexture ,
info - > metalnessTexture ,
info - > roughnessTexture ,
info - > clearcoatTexture ,
info - > normalTexture
} ;
for ( uint32_t i = 0 ; i < COUNTOF ( textures ) ; i + + ) {
2022-06-18 00:43:26 +00:00
lovrRetain ( textures [ i ] ) ;
2022-06-17 06:49:09 +00:00
Texture * texture = textures [ i ] ? textures [ i ] : state . defaultTexture ;
2022-07-04 07:18:38 +00:00
lovrCheck ( i = = 0 | | texture - > info . type = = TEXTURE_2D , " Material textures must be 2D " ) ;
2022-06-17 06:49:09 +00:00
bindings [ i + 1 ] = ( gpu_binding ) { i + 1 , GPU_SLOT_SAMPLED_TEXTURE , . texture = texture - > gpu } ;
2022-08-06 07:50:25 +00:00
material - > hasWritableTexture | = texture - > info . usage ! = TEXTURE_SAMPLE ;
2022-06-17 06:49:09 +00:00
}
gpu_bundle_info bundleInfo = {
. layout = state . layouts . data [ state . materialLayout ] . gpu ,
. bindings = bindings ,
. count = COUNTOF ( bindings )
} ;
gpu_bundle_write ( & material - > bundle , & bundleInfo , 1 ) ;
return material ;
}
void lovrMaterialDestroy ( void * ref ) {
Material * material = ref ;
MaterialBlock * block = & state . materialBlocks . data [ material - > block ] ;
material - > tick = state . tick ;
block - > tail = material - > index ;
if ( block - > head = = ~ 0u ) block - > head = block - > tail ;
lovrRelease ( material - > info . texture , lovrTextureDestroy ) ;
lovrRelease ( material - > info . glowTexture , lovrTextureDestroy ) ;
2022-06-18 00:43:26 +00:00
lovrRelease ( material - > info . occlusionTexture , lovrTextureDestroy ) ;
lovrRelease ( material - > info . metalnessTexture , lovrTextureDestroy ) ;
lovrRelease ( material - > info . roughnessTexture , lovrTextureDestroy ) ;
lovrRelease ( material - > info . clearcoatTexture , lovrTextureDestroy ) ;
lovrRelease ( material - > info . normalTexture , lovrTextureDestroy ) ;
2022-06-17 06:49:09 +00:00
}
const MaterialInfo * lovrMaterialGetInfo ( Material * material ) {
return & material - > info ;
}
2022-06-19 00:43:12 +00:00
// Font
2022-07-30 22:20:01 +00:00
Font * lovrGraphicsGetDefaultFont ( ) {
if ( ! state . defaultFont ) {
Rasterizer * rasterizer = lovrRasterizerCreate ( NULL , 32 ) ;
state . defaultFont = lovrFontCreate ( & ( FontInfo ) {
. rasterizer = rasterizer ,
. spread = 4.
} ) ;
lovrRelease ( rasterizer , lovrRasterizerDestroy ) ;
}
return state . defaultFont ;
}
2022-07-13 07:07:15 +00:00
Font * lovrFontCreate ( const FontInfo * info ) {
2022-06-19 00:43:12 +00:00
Font * font = calloc ( 1 , sizeof ( Font ) ) ;
lovrAssert ( font , " Out of memory " ) ;
font - > ref = 1 ;
font - > info = * info ;
lovrRetain ( info - > rasterizer ) ;
arr_init ( & font - > glyphs , realloc ) ;
2022-06-21 01:26:15 +00:00
map_init ( & font - > glyphLookup , 36 ) ;
map_init ( & font - > kerning , 36 ) ;
2022-06-29 03:18:45 +00:00
font - > pixelDensity = lovrRasterizerGetLeading ( info - > rasterizer ) ;
2022-06-27 04:22:45 +00:00
font - > lineSpacing = 1.f ;
font - > padding = ( uint32_t ) ceil ( info - > spread / 2. ) ;
2022-06-21 01:26:15 +00:00
// Initial atlas size must be big enough to hold any of the glyphs
float box [ 4 ] ;
font - > atlasWidth = 1 ;
font - > atlasHeight = 1 ;
lovrRasterizerGetBoundingBox ( info - > rasterizer , box ) ;
2022-06-27 04:22:45 +00:00
uint32_t maxWidth = ( uint32_t ) ceilf ( box [ 2 ] - box [ 0 ] ) + 2 * font - > padding ;
uint32_t maxHeight = ( uint32_t ) ceilf ( box [ 3 ] - box [ 1 ] ) + 2 * font - > padding ;
while ( font - > atlasWidth < 2 * maxWidth | | font - > atlasHeight < 2 * maxHeight ) {
2022-06-21 01:26:15 +00:00
font - > atlasWidth < < = 1 ;
font - > atlasHeight < < = 1 ;
}
2022-06-19 00:43:12 +00:00
return font ;
}
void lovrFontDestroy ( void * ref ) {
Font * font = ref ;
lovrRelease ( font - > info . rasterizer , lovrRasterizerDestroy ) ;
lovrRelease ( font - > material , lovrMaterialDestroy ) ;
lovrRelease ( font - > atlas , lovrTextureDestroy ) ;
arr_free ( & font - > glyphs ) ;
2022-06-21 01:26:15 +00:00
map_free ( & font - > glyphLookup ) ;
map_free ( & font - > kerning ) ;
2022-06-19 00:43:12 +00:00
free ( font ) ;
}
2022-06-21 22:28:03 +00:00
const FontInfo * lovrFontGetInfo ( Font * font ) {
return & font - > info ;
}
2022-06-19 00:43:12 +00:00
float lovrFontGetPixelDensity ( Font * font ) {
return font - > pixelDensity ;
}
void lovrFontSetPixelDensity ( Font * font , float pixelDensity ) {
font - > pixelDensity = pixelDensity ;
}
2022-06-26 02:54:13 +00:00
float lovrFontGetLineSpacing ( Font * font ) {
return font - > lineSpacing ;
}
void lovrFontSetLineSpacing ( Font * font , float spacing ) {
font - > lineSpacing = spacing ;
}
2022-06-30 03:17:26 +00:00
static Glyph * lovrFontGetGlyph ( Font * font , uint32_t codepoint , bool * resized ) {
2022-06-25 22:26:42 +00:00
uint64_t hash = hash64 ( & codepoint , 4 ) ;
uint64_t index = map_get ( & font - > glyphLookup , hash ) ;
if ( index ! = MAP_NIL ) {
2022-06-30 03:17:26 +00:00
if ( resized ) * resized = false ;
2022-06-25 22:26:42 +00:00
return & font - > glyphs . data [ index ] ;
}
arr_expand ( & font - > glyphs , 1 ) ;
map_set ( & font - > glyphLookup , hash , font - > glyphs . length ) ;
Glyph * glyph = & font - > glyphs . data [ font - > glyphs . length + + ] ;
glyph - > codepoint = codepoint ;
2022-06-29 03:18:45 +00:00
glyph - > advance = lovrRasterizerGetAdvance ( font - > info . rasterizer , codepoint ) ;
2022-06-25 22:26:42 +00:00
if ( lovrRasterizerIsGlyphEmpty ( font - > info . rasterizer , codepoint ) ) {
2022-06-27 03:28:30 +00:00
memset ( glyph - > box , 0 , sizeof ( glyph - > box ) ) ;
2022-06-30 03:17:26 +00:00
if ( resized ) * resized = false ;
2022-06-25 22:26:42 +00:00
return glyph ;
}
lovrRasterizerGetGlyphBoundingBox ( font - > info . rasterizer , codepoint , glyph - > box ) ;
float width = glyph - > box [ 2 ] - glyph - > box [ 0 ] ;
float height = glyph - > box [ 3 ] - glyph - > box [ 1 ] ;
2022-06-27 03:28:30 +00:00
uint32_t pixelWidth = 2 * font - > padding + ( uint32_t ) ceilf ( width ) ;
uint32_t pixelHeight = 2 * font - > padding + ( uint32_t ) ceilf ( height ) ;
2022-06-25 22:26:42 +00:00
2022-06-27 04:22:45 +00:00
// If the glyph exceeds the width, start a new row
2022-06-25 22:26:42 +00:00
if ( font - > atlasX + pixelWidth > font - > atlasWidth ) {
font - > atlasX = font - > atlasWidth = = font - > atlasHeight ? 0 : font - > atlasWidth > > 1 ;
font - > atlasY + = font - > rowHeight ;
2022-06-27 04:22:45 +00:00
}
// If the glyph exceeds the height, expand the atlas
if ( font - > atlasY + pixelHeight > font - > atlasHeight ) {
if ( font - > atlasWidth = = font - > atlasHeight ) {
font - > atlasX = font - > atlasWidth ;
font - > atlasY = 0 ;
font - > atlasWidth < < = 1 ;
font - > rowHeight = 0 ;
} else {
font - > atlasX = 0 ;
font - > atlasY = font - > atlasHeight ;
font - > atlasHeight < < = 1 ;
font - > rowHeight = 0 ;
2022-06-25 22:26:42 +00:00
}
}
2022-06-27 03:28:30 +00:00
glyph - > x = font - > atlasX + font - > padding ;
glyph - > y = font - > atlasY + font - > padding ;
glyph - > uv [ 0 ] = ( uint16_t ) ( ( float ) glyph - > x / font - > atlasWidth * 65535.f + .5f ) ;
glyph - > uv [ 1 ] = ( uint16_t ) ( ( float ) glyph - > y / font - > atlasHeight * 65535.f + .5f ) ;
glyph - > uv [ 2 ] = ( uint16_t ) ( ( float ) ( glyph - > x + width ) / font - > atlasWidth * 65535.f + .5f ) ;
glyph - > uv [ 3 ] = ( uint16_t ) ( ( float ) ( glyph - > y + height ) / font - > atlasHeight * 65535.f + .5f ) ;
2022-06-25 22:26:42 +00:00
font - > atlasX + = pixelWidth ;
2022-06-27 04:22:45 +00:00
font - > rowHeight = MAX ( font - > rowHeight , pixelHeight ) ;
2022-06-25 22:26:42 +00:00
2022-07-12 05:43:42 +00:00
beginFrame ( ) ;
2022-06-27 03:28:30 +00:00
// Atlas resize
2022-06-25 22:26:42 +00:00
if ( ! font - > atlas | | font - > atlasWidth > font - > atlas - > info . width | | font - > atlasHeight > font - > atlas - > info . height ) {
2022-06-30 03:17:26 +00:00
lovrCheck ( font - > atlasWidth < = 65536 , " Font atlas is way too big! " ) ;
2022-06-25 22:26:42 +00:00
Texture * atlas = lovrTextureCreate ( & ( TextureInfo ) {
. type = TEXTURE_2D ,
2022-06-27 03:57:57 +00:00
. format = FORMAT_RGBA8 ,
2022-06-25 22:26:42 +00:00
. width = font - > atlasWidth ,
. height = font - > atlasHeight ,
2022-07-30 22:08:30 +00:00
. layers = 1 ,
2022-06-25 22:26:42 +00:00
. mipmaps = 1 ,
. samples = 1 ,
. usage = TEXTURE_SAMPLE | TEXTURE_TRANSFER ,
. label = " Font Atlas "
} ) ;
float clear [ 4 ] = { 0.f , 0.f , 0.f , 0.f } ;
gpu_clear_texture ( state . stream , atlas - > gpu , clear , 0 , ~ 0u , 0 , ~ 0u ) ;
// This barrier serves 2 purposes:
// - Ensure new atlas clear is finished/flushed before copying to it
// - Ensure any unsynchronized pending uploads to old atlas finish before copying to new atlas
gpu_barrier barrier ;
barrier . prev = GPU_PHASE_TRANSFER ;
barrier . next = GPU_PHASE_TRANSFER ;
barrier . flush = GPU_CACHE_TRANSFER_WRITE ;
barrier . clear = GPU_CACHE_TRANSFER_READ ;
gpu_sync ( state . stream , & barrier , 1 ) ;
if ( font - > atlas ) {
uint32_t srcOffset [ 4 ] = { 0 , 0 , 0 , 0 } ;
uint32_t dstOffset [ 4 ] = { 0 , 0 , 0 , 0 } ;
uint32_t extent [ 3 ] = { font - > atlas - > info . width , font - > atlas - > info . height , 1 } ;
gpu_copy_textures ( state . stream , font - > atlas - > gpu , atlas - > gpu , srcOffset , dstOffset , extent ) ;
lovrRelease ( font - > atlas , lovrTextureDestroy ) ;
}
font - > atlas = atlas ;
2022-06-27 03:28:30 +00:00
// Material
2022-06-25 22:26:42 +00:00
lovrRelease ( font - > material , lovrMaterialDestroy ) ;
font - > material = lovrMaterialCreate ( & ( MaterialInfo ) {
. data . color = { 1.f , 1.f , 1.f , 1.f } ,
. data . uvScale = { 1.f , 1.f } ,
. data . sdfRange = { font - > info . spread / font - > atlasWidth , font - > info . spread / font - > atlasHeight } ,
. texture = font - > atlas
} ) ;
2022-06-27 03:28:30 +00:00
// Recompute all glyph uvs after atlas resize
for ( size_t i = 0 ; i < font - > glyphs . length ; i + + ) {
2022-06-30 03:17:26 +00:00
Glyph * g = & font - > glyphs . data [ i ] ;
2022-06-30 07:29:52 +00:00
if ( g - > box [ 2 ] - g - > box [ 0 ] > 0.f ) {
g - > uv [ 0 ] = ( uint16_t ) ( ( float ) g - > x / font - > atlasWidth * 65535.f + .5f ) ;
g - > uv [ 1 ] = ( uint16_t ) ( ( float ) g - > y / font - > atlasHeight * 65535.f + .5f ) ;
g - > uv [ 2 ] = ( uint16_t ) ( ( float ) ( g - > x + g - > box [ 2 ] - g - > box [ 0 ] ) / font - > atlasWidth * 65535.f + .5f ) ;
g - > uv [ 3 ] = ( uint16_t ) ( ( float ) ( g - > y + g - > box [ 3 ] - g - > box [ 1 ] ) / font - > atlasHeight * 65535.f + .5f ) ;
}
2022-06-27 03:28:30 +00:00
}
2022-06-30 03:17:26 +00:00
if ( resized ) * resized = true ;
2022-06-25 22:26:42 +00:00
}
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
2022-08-07 01:05:30 +00:00
size_t stack = tempPush ( ) ;
2022-06-30 03:17:26 +00:00
float * pixels = tempAlloc ( pixelWidth * pixelHeight * 4 * sizeof ( float ) ) ;
lovrRasterizerGetPixels ( font - > info . rasterizer , glyph - > codepoint , pixels , pixelWidth , pixelHeight , font - > info . spread ) ;
uint8_t * dst = gpu_map ( scratchpad , pixelWidth * pixelHeight * 4 * sizeof ( uint8_t ) , 4 , GPU_MAP_WRITE ) ;
float * src = pixels ;
for ( uint32_t y = 0 ; y < pixelHeight ; y + + ) {
for ( uint32_t x = 0 ; x < pixelWidth ; x + + ) {
for ( uint32_t c = 0 ; c < 4 ; c + + ) {
float f = * src + + ; // CLAMP would evaluate this multiple times
* dst + + = ( uint8_t ) ( CLAMP ( f , 0.f , 1.f ) * 255.f + .5f ) ;
2022-06-27 03:57:57 +00:00
}
}
2022-06-25 22:26:42 +00:00
}
2022-06-30 03:17:26 +00:00
uint32_t dstOffset [ 4 ] = { glyph - > x - font - > padding , glyph - > y - font - > padding , 0 , 0 } ;
uint32_t extent [ 3 ] = { pixelWidth , pixelHeight , 1 } ;
gpu_copy_buffer_texture ( state . stream , scratchpad , font - > atlas - > gpu , 0 , dstOffset , extent ) ;
tempPop ( stack ) ;
2022-06-25 22:26:42 +00:00
state . hasGlyphUpload = true ;
2022-06-30 03:17:26 +00:00
return glyph ;
2022-06-25 22:26:42 +00:00
}
2022-07-04 22:22:54 +00:00
float lovrFontGetKerning ( Font * font , uint32_t first , uint32_t second ) {
uint32_t codepoints [ ] = { first , second } ;
2022-06-25 22:26:42 +00:00
uint64_t hash = hash64 ( codepoints , sizeof ( codepoints ) ) ;
union { float f32 ; uint64_t u64 ; } kerning = { . u64 = map_get ( & font - > kerning , hash ) } ;
if ( kerning . u64 = = MAP_NIL ) {
2022-07-04 22:22:54 +00:00
kerning . f32 = lovrRasterizerGetKerning ( font - > info . rasterizer , first , second ) ;
2022-06-25 22:26:42 +00:00
map_set ( & font - > kerning , hash , kerning . u64 ) ;
}
return kerning . f32 ;
}
2022-07-01 00:25:47 +00:00
float lovrFontGetWidth ( Font * font , ColoredString * strings , uint32_t count ) {
float x = 0.f ;
float maxWidth = 0.f ;
float space = lovrFontGetGlyph ( font , ' ' , NULL ) - > advance ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
size_t bytes ;
uint32_t codepoint ;
uint32_t previous = ' \0 ' ;
const char * str = strings [ i ] . string ;
const char * end = strings [ i ] . string + strings [ i ] . length ;
while ( ( bytes = utf8_decode ( str , end , & codepoint ) ) > 0 ) {
if ( codepoint = = ' ' | | codepoint = = ' \t ' ) {
x + = codepoint = = ' \t ' ? space * 4.f : space ;
previous = ' \0 ' ;
str + = bytes ;
continue ;
} else if ( codepoint = = ' \n ' ) {
maxWidth = MAX ( maxWidth , x ) ;
x = 0.f ;
previous = ' \0 ' ;
str + = bytes ;
continue ;
} else if ( codepoint = = ' \r ' ) {
str + = bytes ;
continue ;
}
Glyph * glyph = lovrFontGetGlyph ( font , codepoint , NULL ) ;
if ( previous ) x + = lovrFontGetKerning ( font , previous , codepoint ) ;
previous = codepoint ;
x + = glyph - > advance ;
str + = bytes ;
}
}
return MAX ( maxWidth , x ) / font - > pixelDensity ;
}
2022-07-01 00:07:47 +00:00
void lovrFontGetLines ( Font * font , ColoredString * strings , uint32_t count , float wrap , void ( * callback ) ( void * context , const char * string , size_t length ) , void * context ) {
2022-06-30 03:17:26 +00:00
size_t totalLength = 0 ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
totalLength + = strings [ i ] . length ;
}
2022-07-12 05:43:42 +00:00
beginFrame ( ) ;
2022-08-07 01:05:30 +00:00
size_t stack = tempPush ( ) ;
2022-06-30 03:17:26 +00:00
char * string = tempAlloc ( totalLength + 1 ) ;
string [ totalLength ] = ' \0 ' ;
2022-08-07 01:05:30 +00:00
size_t cursor = 0 ;
for ( uint32_t i = 0 ; i < count ; cursor + = strings [ i ] . length , i + + ) {
2022-06-30 03:17:26 +00:00
memcpy ( string + cursor , strings [ i ] . string , strings [ i ] . length ) ;
}
float x = 0.f ;
float nextWordStartX = 0.f ;
wrap * = font - > pixelDensity ;
size_t bytes ;
uint32_t codepoint ;
uint32_t previous = ' \0 ' ;
const char * lineStart = string ;
const char * wordStart = string ;
const char * end = string + totalLength ;
2022-06-30 07:29:52 +00:00
float space = lovrFontGetGlyph ( font , ' ' , NULL ) - > advance ;
2022-06-30 03:17:26 +00:00
while ( ( bytes = utf8_decode ( string , end , & codepoint ) ) > 0 ) {
if ( codepoint = = ' ' | | codepoint = = ' \t ' ) {
2022-06-30 07:29:52 +00:00
x + = codepoint = = ' \t ' ? space * 4.f : space ;
2022-06-30 03:17:26 +00:00
nextWordStartX = x ;
previous = ' \0 ' ;
string + = bytes ;
wordStart = string ;
continue ;
} else if ( codepoint = = ' \n ' ) {
size_t length = string - lineStart ;
while ( string [ length ] = = ' ' | | string [ length ] = = ' \t ' ) length - - ;
callback ( context , lineStart , length ) ;
nextWordStartX = 0.f ;
x = 0.f ;
previous = ' \0 ' ;
string + = bytes ;
lineStart = string ;
wordStart = string ;
continue ;
} else if ( codepoint = = ' \r ' ) {
string + = bytes ;
continue ;
}
Glyph * glyph = lovrFontGetGlyph ( font , codepoint , NULL ) ;
// Keming
if ( previous ) x + = lovrFontGetKerning ( font , previous , codepoint ) ;
previous = codepoint ;
// Wrap
2022-07-01 00:07:47 +00:00
if ( wordStart ! = lineStart & & x + glyph - > advance > wrap ) {
2022-06-30 03:17:26 +00:00
size_t length = wordStart - lineStart ;
while ( string [ length ] = = ' ' | | string [ length ] = = ' \t ' ) length - - ;
callback ( context , lineStart , length ) ;
lineStart = wordStart ;
x - = nextWordStartX ;
nextWordStartX = 0.f ;
previous = ' \0 ' ;
}
// Advance
x + = glyph - > advance ;
string + = bytes ;
}
if ( end - lineStart > 0 ) {
callback ( context , lineStart , end - lineStart ) ;
}
tempPop ( stack ) ;
}
2022-07-17 23:38:29 +00:00
static void aline ( GlyphVertex * vertices , uint32_t head , uint32_t tail , float width , HorizontalAlign align ) {
if ( align = = ALIGN_LEFT ) return ;
float shift = align / 2.f * width ;
for ( uint32_t i = head ; i < tail ; i + + ) {
vertices [ i ] . position . x - = shift ;
}
}
void lovrFontGetVertices ( Font * font , ColoredString * strings , uint32_t count , float wrap , HorizontalAlign halign , VerticalAlign valign , GlyphVertex * vertices , uint32_t * glyphCount , uint32_t * lineCount , Material * * material , bool flip ) {
uint32_t vertexCount = 0 ;
uint32_t lineStart = 0 ;
uint32_t wordStart = 0 ;
* glyphCount = 0 ;
* lineCount = 1 ;
float x = 0.f ;
float y = 0.f ;
float wordStartX = 0.f ;
float prevWordEndX = 0.f ;
float leading = lovrRasterizerGetLeading ( font - > info . rasterizer ) * font - > lineSpacing ;
float space = lovrFontGetGlyph ( font , ' ' , NULL ) - > advance ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
size_t bytes ;
uint32_t codepoint ;
uint32_t previous = ' \0 ' ;
const char * str = strings [ i ] . string ;
const char * end = strings [ i ] . string + strings [ i ] . length ;
uint8_t r = ( uint8_t ) ( CLAMP ( strings [ i ] . color [ 0 ] , 0.f , 1.f ) * 255.f ) ;
uint8_t g = ( uint8_t ) ( CLAMP ( strings [ i ] . color [ 1 ] , 0.f , 1.f ) * 255.f ) ;
uint8_t b = ( uint8_t ) ( CLAMP ( strings [ i ] . color [ 2 ] , 0.f , 1.f ) * 255.f ) ;
uint8_t a = ( uint8_t ) ( CLAMP ( strings [ i ] . color [ 3 ] , 0.f , 1.f ) * 255.f ) ;
while ( ( bytes = utf8_decode ( str , end , & codepoint ) ) > 0 ) {
if ( codepoint = = ' ' | | codepoint = = ' \t ' ) {
if ( previous ) prevWordEndX = x ;
wordStart = vertexCount ;
x + = codepoint = = ' \t ' ? space * 4.f : space ;
wordStartX = x ;
previous = ' \0 ' ;
str + = bytes ;
continue ;
} else if ( codepoint = = ' \n ' ) {
aline ( vertices , lineStart , vertexCount , x , halign ) ;
lineStart = vertexCount ;
wordStart = vertexCount ;
x = 0.f ;
y - = leading ;
wordStartX = 0.f ;
prevWordEndX = 0.f ;
( * lineCount ) + + ;
previous = ' \0 ' ;
str + = bytes ;
continue ;
} else if ( codepoint = = ' \r ' ) {
str + = bytes ;
continue ;
}
bool resized ;
Glyph * glyph = lovrFontGetGlyph ( font , codepoint , & resized ) ;
if ( resized ) {
lovrFontGetVertices ( font , strings , count , wrap , halign , valign , vertices , glyphCount , lineCount , material , flip ) ;
return ;
}
// Keming
if ( previous ) x + = lovrFontGetKerning ( font , previous , codepoint ) ;
previous = codepoint ;
// Wrap
if ( wrap > 0.f & & x + glyph - > advance > wrap & & wordStart ! = lineStart ) {
float dx = wordStartX ;
float dy = leading ;
// Shift the vertices of the overflowing word down a line and back to the beginning
for ( uint32_t v = wordStart ; v < vertexCount ; v + + ) {
vertices [ v ] . position . x - = dx ;
vertices [ v ] . position . y - = dy ;
}
aline ( vertices , lineStart , wordStart , prevWordEndX , halign ) ;
lineStart = wordStart ;
wordStartX = 0.f ;
( * lineCount ) + + ;
x - = dx ;
y - = dy ;
}
// Vertices
float * bb = glyph - > box ;
uint16_t * uv = glyph - > uv ;
if ( flip ) {
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , - ( y + bb [ 1 ] ) } , { uv [ 0 ] , uv [ 3 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , - ( y + bb [ 1 ] ) } , { uv [ 2 ] , uv [ 3 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , - ( y + bb [ 3 ] ) } , { uv [ 0 ] , uv [ 1 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , - ( y + bb [ 3 ] ) } , { uv [ 2 ] , uv [ 1 ] } , { r , g , b , a } } ;
} else {
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , y + bb [ 3 ] } , { uv [ 0 ] , uv [ 1 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , y + bb [ 3 ] } , { uv [ 2 ] , uv [ 1 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , y + bb [ 1 ] } , { uv [ 0 ] , uv [ 3 ] } , { r , g , b , a } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , y + bb [ 1 ] } , { uv [ 2 ] , uv [ 3 ] } , { r , g , b , a } } ;
}
( * glyphCount ) + + ;
// Advance
x + = glyph - > advance ;
str + = bytes ;
}
}
// Align last line
aline ( vertices , lineStart , vertexCount , x , halign ) ;
* material = font - > material ;
}
2022-07-04 00:26:31 +00:00
// Model
2022-07-13 07:07:15 +00:00
Model * lovrModelCreate ( const ModelInfo * info ) {
2022-07-04 00:26:31 +00:00
ModelData * data = info - > data ;
Model * model = calloc ( 1 , sizeof ( Model ) ) ;
lovrAssert ( model , " Out of memory " ) ;
model - > ref = 1 ;
model - > info = * info ;
lovrRetain ( info - > data ) ;
2022-07-17 19:04:47 +00:00
// Materials and Textures
model - > textures = calloc ( data - > imageCount , sizeof ( Texture * ) ) ;
2022-07-04 00:26:31 +00:00
model - > materials = malloc ( data - > materialCount * sizeof ( Material * ) ) ;
2022-07-17 19:04:47 +00:00
lovrAssert ( model - > textures & & model - > materials , " Out of memory " ) ;
2022-07-04 00:26:31 +00:00
for ( uint32_t i = 0 ; i < data - > materialCount ; i + + ) {
MaterialInfo material ;
ModelMaterial * properties = & data - > materials [ i ] ;
memcpy ( & material . data , properties , sizeof ( MaterialData ) ) ;
2022-07-17 19:04:47 +00:00
struct { uint32_t index ; Texture * * texture ; } textures [ ] = {
{ properties - > texture , & material . texture } ,
{ properties - > glowTexture , & material . glowTexture } ,
{ properties - > occlusionTexture , & material . occlusionTexture } ,
{ properties - > metalnessTexture , & material . metalnessTexture } ,
{ properties - > roughnessTexture , & material . roughnessTexture } ,
{ properties - > clearcoatTexture , & material . clearcoatTexture } ,
{ properties - > normalTexture , & material . normalTexture }
} ;
for ( uint32_t t = 0 ; t < COUNTOF ( textures ) ; t + + ) {
uint32_t index = textures [ t ] . index ;
Texture * * texture = textures [ t ] . texture ;
if ( index = = ~ 0u ) {
* texture = NULL ;
} else {
if ( ! model - > textures [ index ] ) {
model - > textures [ index ] = lovrTextureCreate ( & ( TextureInfo ) {
. type = TEXTURE_2D ,
. usage = TEXTURE_SAMPLE ,
. format = lovrImageGetFormat ( data - > images [ index ] ) ,
. width = lovrImageGetWidth ( data - > images [ index ] , 0 ) ,
. height = lovrImageGetHeight ( data - > images [ index ] , 0 ) ,
2022-07-30 22:08:30 +00:00
. layers = 1 ,
2022-07-17 19:04:47 +00:00
. mipmaps = info - > mipmaps | | lovrImageGetLevelCount ( data - > images [ index ] ) > 1 ? ~ 0u : 1 ,
. samples = 1 ,
. srgb = texture = = & material . texture | | texture = = & material . glowTexture ,
. images = & data - > images [ index ] ,
. imageCount = 1
} ) ;
}
* texture = model - > textures [ index ] ;
}
}
2022-07-04 00:26:31 +00:00
model - > materials [ i ] = lovrMaterialCreate ( & material ) ;
}
// Buffers
2022-08-07 02:23:41 +00:00
char * vertices = NULL ;
char * indices = NULL ;
char * skinData = NULL ;
2022-07-04 00:26:31 +00:00
BufferInfo vertexBufferInfo = {
. length = data - > vertexCount ,
. stride = sizeof ( ModelVertex ) ,
. fieldCount = 5 ,
. fields [ 0 ] = { 0 , 10 , FIELD_F32x3 , offsetof ( ModelVertex , position ) } ,
. fields [ 1 ] = { 0 , 11 , FIELD_F32x3 , offsetof ( ModelVertex , normal ) } ,
. fields [ 2 ] = { 0 , 12 , FIELD_F32x2 , offsetof ( ModelVertex , uv ) } ,
. fields [ 3 ] = { 0 , 13 , FIELD_UN8x4 , offsetof ( ModelVertex , color ) } ,
. fields [ 4 ] = { 0 , 14 , FIELD_F32x3 , offsetof ( ModelVertex , tangent ) }
} ;
model - > vertexBuffer = lovrBufferCreate ( & vertexBufferInfo , ( void * * ) & vertices ) ;
if ( data - > skinnedVertexCount > 0 ) {
model - > skinBuffer = lovrBufferCreate ( & ( BufferInfo ) {
. length = data - > skinnedVertexCount ,
. stride = 8 ,
. fieldCount = 2 ,
. fields [ 0 ] = { 0 , 0 , FIELD_UN8x4 , 0 } ,
. fields [ 1 ] = { 0 , 0 , FIELD_U8x4 , 4 }
} , ( void * * ) & skinData ) ;
vertexBufferInfo . length = data - > skinnedVertexCount ;
model - > rawVertexBuffer = lovrBufferCreate ( & vertexBufferInfo , NULL ) ;
beginFrame ( ) ;
gpu_buffer * src = model - > vertexBuffer - > gpu ;
gpu_buffer * dst = model - > rawVertexBuffer - > gpu ;
gpu_copy_buffers ( state . stream , src , dst , 0 , 0 , data - > skinnedVertexCount * sizeof ( ModelVertex ) ) ;
gpu_barrier barrier ;
barrier . prev = GPU_PHASE_TRANSFER ;
barrier . next = GPU_PHASE_SHADER_COMPUTE ;
barrier . flush = GPU_CACHE_TRANSFER_WRITE ;
barrier . clear = GPU_CACHE_STORAGE_READ | GPU_CACHE_STORAGE_WRITE ;
gpu_sync ( state . stream , & barrier , 1 ) ;
}
2022-08-07 01:05:30 +00:00
uint32_t indexSize = data - > indexType = = U32 ? 4 : 2 ;
2022-07-04 00:26:31 +00:00
if ( data - > indexCount > 0 ) {
model - > indexBuffer = lovrBufferCreate ( & ( BufferInfo ) {
. length = data - > indexCount ,
. stride = indexSize ,
. fieldCount = 1 ,
2022-07-10 19:18:31 +00:00
. fields [ 0 ] = { 0 , 0 , data - > indexType = = U32 ? FIELD_INDEX32 : FIELD_INDEX16 , 0 }
2022-07-04 00:26:31 +00:00
} , ( void * * ) & indices ) ;
}
// Sort primitives by their skin, so there is a single contiguous region of skinned vertices
2022-08-07 01:05:30 +00:00
size_t stack = tempPush ( ) ;
2022-07-04 00:26:31 +00:00
uint64_t * map = tempAlloc ( data - > primitiveCount * sizeof ( uint64_t ) ) ;
for ( uint32_t i = 0 ; i < data - > primitiveCount ; i + + ) {
map [ i ] = ( ( uint64_t ) data - > primitives [ i ] . skin < < 32 ) | i ;
}
qsort ( map , data - > primitiveCount , sizeof ( uint64_t ) , u64cmp ) ;
// Draws
model - > draws = calloc ( data - > primitiveCount , sizeof ( Draw ) ) ;
lovrAssert ( model - > draws , " Out of memory " ) ;
for ( uint32_t i = 0 , vertexCursor = 0 , indexCursor = 0 ; i < data - > primitiveCount ; i + + ) {
ModelPrimitive * primitive = & data - > primitives [ map [ i ] & ~ 0u ] ;
Draw * draw = & model - > draws [ map [ i ] & ~ 0u ] ;
switch ( primitive - > mode ) {
2022-07-13 02:35:23 +00:00
case DRAW_POINTS : draw - > mode = MESH_POINTS ; break ;
case DRAW_LINES : draw - > mode = MESH_LINES ; break ;
case DRAW_TRIANGLES : draw - > mode = MESH_TRIANGLES ; break ;
2022-07-04 00:26:31 +00:00
default : lovrThrow ( " Model uses an unsupported draw mode (lineloop, linestrip, strip, fan) " ) ;
}
draw - > material = primitive - > material = = ~ 0u ? NULL : model - > materials [ primitive - > material ] ;
draw - > vertex . buffer = model - > vertexBuffer ;
if ( primitive - > indices ) {
draw - > index . buffer = model - > indexBuffer ;
draw - > start = indexCursor ;
draw - > count = primitive - > indices - > count ;
draw - > base = vertexCursor ;
indexCursor + = draw - > count ;
} else {
draw - > start = vertexCursor ;
draw - > count = primitive - > attributes [ ATTR_POSITION ] - > count ;
}
vertexCursor + = primitive - > attributes [ ATTR_POSITION ] - > count ;
}
// Vertices
for ( uint32_t i = 0 ; i < data - > primitiveCount ; i + + ) {
ModelPrimitive * primitive = & data - > primitives [ map [ i ] & ~ 0u ] ;
ModelAttribute * * attributes = primitive - > attributes ;
uint32_t count = attributes [ ATTR_POSITION ] - > count ;
size_t stride = sizeof ( ModelVertex ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_POSITION ] , vertices + 0 , F32 , 3 , false , count , stride , 0 ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_NORMAL ] , vertices + 12 , F32 , 3 , false , count , stride , 0 ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_TEXCOORD ] , vertices + 24 , F32 , 2 , false , count , stride , 0 ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_COLOR ] , vertices + 32 , U8 , 4 , true , count , stride , 255 ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_TANGENT ] , vertices + 36 , F32 , 3 , false , count , stride , 0 ) ;
vertices + = count * stride ;
if ( data - > skinnedVertexCount > 0 & & primitive - > skin ! = ~ 0u ) {
lovrModelDataCopyAttribute ( data , attributes [ ATTR_JOINTS ] , skinData + 0 , U8 , 4 , false , count , 8 , 0 ) ;
lovrModelDataCopyAttribute ( data , attributes [ ATTR_WEIGHTS ] , skinData + 4 , U8 , 4 , true , count , 8 , 0 ) ;
skinData + = count * 8 ;
}
if ( primitive - > indices ) {
char * indexData = data - > buffers [ primitive - > indices - > buffer ] . data + primitive - > indices - > offset ;
memcpy ( indices , indexData , primitive - > indices - > count * indexSize ) ;
indices + = primitive - > indices - > count * indexSize ;
}
}
for ( uint32_t i = 0 ; i < data - > skinCount ; i + + ) {
lovrCheck ( data - > skins [ i ] . jointCount < = 256 , " Currently, the max number of joints per skin is 256 " ) ;
}
model - > localTransforms = malloc ( sizeof ( NodeTransform ) * data - > nodeCount ) ;
model - > globalTransforms = malloc ( 16 * sizeof ( float ) * data - > nodeCount ) ;
lovrAssert ( model - > localTransforms & & model - > globalTransforms , " Out of memory " ) ;
2022-07-13 02:35:23 +00:00
lovrModelResetNodeTransforms ( model ) ;
2022-07-04 00:26:31 +00:00
tempPop ( stack ) ;
return model ;
}
void lovrModelDestroy ( void * ref ) {
Model * model = ref ;
ModelData * data = model - > info . data ;
for ( uint32_t i = 0 ; i < data - > materialCount ; i + + ) {
lovrRelease ( model - > materials [ i ] , lovrMaterialDestroy ) ;
}
for ( uint32_t i = 0 ; i < data - > imageCount ; i + + ) {
lovrRelease ( model - > textures [ i ] , lovrTextureDestroy ) ;
}
lovrRelease ( model - > rawVertexBuffer , lovrBufferDestroy ) ;
lovrRelease ( model - > vertexBuffer , lovrBufferDestroy ) ;
lovrRelease ( model - > indexBuffer , lovrBufferDestroy ) ;
lovrRelease ( model - > skinBuffer , lovrBufferDestroy ) ;
lovrRelease ( model - > info . data , lovrModelDataDestroy ) ;
free ( model - > localTransforms ) ;
free ( model - > globalTransforms ) ;
free ( model - > draws ) ;
free ( model - > materials ) ;
free ( model - > textures ) ;
free ( model ) ;
}
2022-07-07 02:22:02 +00:00
const ModelInfo * lovrModelGetInfo ( Model * model ) {
return & model - > info ;
2022-07-04 00:26:31 +00:00
}
2022-07-13 02:35:23 +00:00
uint32_t lovrModelGetNodeDrawCount ( Model * model , uint32_t node ) {
ModelData * data = model - > info . data ;
lovrCheck ( node < data - > nodeCount , " Invalid model node index %d " , node + 1 ) ;
return data - > nodes [ node ] . primitiveCount ;
}
void lovrModelGetNodeDraw ( Model * model , uint32_t node , uint32_t index , ModelDraw * mesh ) {
ModelData * data = model - > info . data ;
lovrCheck ( node < data - > nodeCount , " Invalid model node index %d " , node + 1 ) ;
lovrCheck ( index < data - > nodes [ node ] . primitiveCount , " Invalid model node draw index %d " , index + 1 ) ;
Draw * draw = & model - > draws [ data - > nodes [ node ] . primitiveIndex + index ] ;
mesh - > mode = draw - > mode ;
mesh - > material = draw - > material ;
mesh - > start = draw - > start ;
mesh - > count = draw - > count ;
mesh - > base = draw - > base ;
mesh - > indexed = draw - > index . buffer ;
}
void lovrModelResetNodeTransforms ( Model * model ) {
2022-07-04 00:26:31 +00:00
ModelData * data = model - > info . data ;
for ( uint32_t i = 0 ; i < data - > nodeCount ; i + + ) {
vec3 position = model - > localTransforms [ i ] . properties [ PROP_TRANSLATION ] ;
quat orientation = model - > localTransforms [ i ] . properties [ PROP_ROTATION ] ;
vec3 scale = model - > localTransforms [ i ] . properties [ PROP_SCALE ] ;
2022-08-03 06:03:52 +00:00
if ( data - > nodes [ i ] . hasMatrix ) {
2022-07-04 00:26:31 +00:00
mat4_getPosition ( data - > nodes [ i ] . transform . matrix , position ) ;
mat4_getOrientation ( data - > nodes [ i ] . transform . matrix , orientation ) ;
mat4_getScale ( data - > nodes [ i ] . transform . matrix , scale ) ;
} else {
2022-08-03 06:03:52 +00:00
vec3_init ( position , data - > nodes [ i ] . transform . translation ) ;
quat_init ( orientation , data - > nodes [ i ] . transform . rotation ) ;
vec3_init ( scale , data - > nodes [ i ] . transform . scale ) ;
2022-07-04 00:26:31 +00:00
}
}
model - > transformsDirty = true ;
}
void lovrModelAnimate ( Model * model , uint32_t animationIndex , float time , float alpha ) {
if ( alpha < = 0.f ) return ;
ModelData * data = model - > info . data ;
lovrAssert ( animationIndex < data - > animationCount , " Invalid animation index '%d' (Model has %d animation%s) " , animationIndex + 1 , data - > animationCount , data - > animationCount = = 1 ? " " : " s " ) ;
ModelAnimation * animation = & data - > animations [ animationIndex ] ;
time = fmodf ( time , animation - > duration ) ;
for ( uint32_t i = 0 ; i < animation - > channelCount ; i + + ) {
ModelAnimationChannel * channel = & animation - > channels [ i ] ;
uint32_t node = channel - > nodeIndex ;
NodeTransform * transform = & model - > localTransforms [ node ] ;
uint32_t keyframe = 0 ;
while ( keyframe < channel - > keyframeCount & & channel - > times [ keyframe ] < time ) {
keyframe + + ;
}
float property [ 4 ] ;
bool rotate = channel - > property = = PROP_ROTATION ;
size_t n = 3 + rotate ;
float * ( * lerp ) ( float * a , float * b , float t ) = rotate ? quat_slerp : vec3_lerp ;
// Handle the first/last keyframe case (no interpolation)
if ( keyframe = = 0 | | keyframe > = channel - > keyframeCount ) {
size_t index = MIN ( keyframe , channel - > keyframeCount - 1 ) ;
// For cubic interpolation, each keyframe has 3 parts, and the actual data is in the middle
if ( channel - > smoothing = = SMOOTH_CUBIC ) {
index = 3 * index + 1 ;
}
memcpy ( property , channel - > data + index * n , n * sizeof ( float ) ) ;
} else {
float t1 = channel - > times [ keyframe - 1 ] ;
float t2 = channel - > times [ keyframe ] ;
float z = ( time - t1 ) / ( t2 - t1 ) ;
switch ( channel - > smoothing ) {
case SMOOTH_STEP :
memcpy ( property , channel - > data + ( z > = .5f ? keyframe : keyframe - 1 ) * n , n * sizeof ( float ) ) ;
break ;
case SMOOTH_LINEAR :
memcpy ( property , channel - > data + ( keyframe - 1 ) * n , n * sizeof ( float ) ) ;
lerp ( property , channel - > data + keyframe * n , z ) ;
break ;
case SMOOTH_CUBIC : {
size_t stride = 3 * n ;
float * p0 = channel - > data + ( keyframe - 1 ) * stride + 1 * n ;
float * m0 = channel - > data + ( keyframe - 1 ) * stride + 2 * n ;
float * p1 = channel - > data + ( keyframe - 0 ) * stride + 1 * n ;
float * m1 = channel - > data + ( keyframe - 0 ) * stride + 0 * n ;
float dt = t2 - t1 ;
float z2 = z * z ;
float z3 = z2 * z ;
float a = 2.f * z3 - 3.f * z2 + 1.f ;
float b = 2.f * z3 - 3.f * z2 + 1.f ;
float c = - 2.f * z3 + 3.f * z2 ;
float d = ( z3 * - z2 ) * dt ;
for ( size_t j = 0 ; j < n ; j + + ) {
property [ j ] = a * p0 [ j ] + b * m0 [ j ] + c * p1 [ j ] + d * m1 [ j ] ;
}
break ;
}
default : break ;
}
}
if ( alpha > = 1.f ) {
memcpy ( transform - > properties [ channel - > property ] , property , n * sizeof ( float ) ) ;
} else {
lerp ( transform - > properties [ channel - > property ] , property , alpha ) ;
}
}
model - > transformsDirty = true ;
}
2022-07-13 02:35:23 +00:00
void lovrModelGetNodeTransform ( Model * model , uint32_t node , float position [ 4 ] , float scale [ 4 ] , float rotation [ 4 ] , CoordinateSpace space ) {
2022-07-04 00:26:31 +00:00
if ( space = = SPACE_LOCAL ) {
vec3_init ( position , model - > localTransforms [ node ] . properties [ PROP_TRANSLATION ] ) ;
2022-07-13 02:35:23 +00:00
vec3_init ( scale , model - > localTransforms [ node ] . properties [ PROP_SCALE ] ) ;
2022-07-04 00:26:31 +00:00
quat_init ( rotation , model - > localTransforms [ node ] . properties [ PROP_ROTATION ] ) ;
} else {
if ( model - > transformsDirty ) {
2022-07-13 02:35:23 +00:00
updateModelTransforms ( model , model - > info . data - > rootNode , ( float [ ] ) MAT4_IDENTITY ) ;
2022-07-04 00:26:31 +00:00
model - > transformsDirty = false ;
}
mat4_getPosition ( model - > globalTransforms + 16 * node , position ) ;
2022-07-13 02:35:23 +00:00
mat4_getScale ( model - > globalTransforms + 16 * node , scale ) ;
2022-07-04 00:26:31 +00:00
mat4_getOrientation ( model - > globalTransforms + 16 * node , rotation ) ;
}
}
2022-07-13 02:35:23 +00:00
void lovrModelSetNodeTransform ( Model * model , uint32_t node , float position [ 4 ] , float scale [ 4 ] , float rotation [ 4 ] , float alpha ) {
2022-07-04 00:26:31 +00:00
if ( alpha < = 0.f ) return ;
NodeTransform * transform = & model - > localTransforms [ node ] ;
if ( alpha > = 1.f ) {
2022-07-13 02:35:23 +00:00
if ( position ) vec3_init ( transform - > properties [ PROP_TRANSLATION ] , position ) ;
if ( scale ) vec3_init ( transform - > properties [ PROP_SCALE ] , scale ) ;
if ( rotation ) quat_init ( transform - > properties [ PROP_ROTATION ] , rotation ) ;
2022-07-04 00:26:31 +00:00
} else {
2022-07-13 02:35:23 +00:00
if ( position ) vec3_lerp ( transform - > properties [ PROP_TRANSLATION ] , position , alpha ) ;
if ( scale ) vec3_lerp ( transform - > properties [ PROP_SCALE ] , scale , alpha ) ;
if ( rotation ) quat_slerp ( transform - > properties [ PROP_ROTATION ] , rotation , alpha ) ;
2022-07-04 00:26:31 +00:00
}
model - > transformsDirty = true ;
}
Texture * lovrModelGetTexture ( Model * model , uint32_t index ) {
ModelData * data = model - > info . data ;
lovrAssert ( index < data - > imageCount , " Invalid texture index '%d' (Model has %d texture%s) " , index , data - > imageCount , data - > imageCount = = 1 ? " " : " s " ) ;
return model - > textures [ index ] ;
}
Material * lovrModelGetMaterial ( Model * model , uint32_t index ) {
ModelData * data = model - > info . data ;
lovrAssert ( index < data - > materialCount , " Invalid material index '%d' (Model has %d material%s) " , index , data - > materialCount , data - > materialCount = = 1 ? " " : " s " ) ;
return model - > materials [ index ] ;
}
Buffer * lovrModelGetVertexBuffer ( Model * model ) {
return model - > rawVertexBuffer ;
}
Buffer * lovrModelGetIndexBuffer ( Model * model ) {
return model - > indexBuffer ;
}
static void lovrModelReskin ( Model * model ) {
ModelData * data = model - > info . data ;
if ( data - > skinCount = = 0 | | model - > lastReskin = = state . tick ) {
return ;
}
if ( ! state . animator ) {
state . animator = lovrShaderCreate ( & ( ShaderInfo ) {
. type = SHADER_COMPUTE ,
2022-07-10 06:09:02 +00:00
. source [ 0 ] = { lovr_shader_animator_comp , sizeof ( lovr_shader_animator_comp ) } ,
2022-07-04 00:26:31 +00:00
. flags = & ( ShaderFlag ) { " local_size_x_id " , 0 , state . device . subgroupSize } ,
2022-07-10 06:09:02 +00:00
. label = " animator "
2022-07-04 00:26:31 +00:00
} ) ;
}
2022-08-07 01:05:30 +00:00
gpu_pipeline * pipeline = state . pipelines . data [ state . animator - > computePipelineIndex ] ;
2022-07-04 00:26:31 +00:00
gpu_layout * layout = state . layouts . data [ state . animator - > layout ] . gpu ;
gpu_shader * shader = state . animator - > gpu ;
gpu_buffer * joints = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
uint32_t count = data - > skinnedVertexCount ;
gpu_binding bindings [ ] = {
{ 0 , GPU_SLOT_STORAGE_BUFFER , . buffer = { model - > rawVertexBuffer - > gpu , 0 , count * sizeof ( ModelVertex ) } } ,
{ 1 , GPU_SLOT_STORAGE_BUFFER , . buffer = { model - > vertexBuffer - > gpu , 0 , count * sizeof ( ModelVertex ) } } ,
{ 2 , GPU_SLOT_STORAGE_BUFFER , . buffer = { model - > skinBuffer - > gpu , 0 , count * 8 } } ,
{ 3 , GPU_SLOT_UNIFORM_BUFFER , . buffer = { joints , 0 , 0 } } // Filled in for each skin
} ;
for ( uint32_t i = 0 , baseVertex = 0 ; i < data - > skinCount ; i + + ) {
ModelSkin * skin = & data - > skins [ i ] ;
float transform [ 16 ] ;
uint32_t size = bindings [ 3 ] . buffer . extent = skin - > jointCount * 16 * sizeof ( float ) ;
float * joint = gpu_map ( joints , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
for ( uint32_t j = 0 ; j < skin - > jointCount ; j + + ) {
mat4_init ( transform , model - > globalTransforms + 16 * skin - > joints [ j ] ) ;
mat4_mul ( transform , skin - > inverseBindMatrices + 16 * j ) ;
memcpy ( joint , transform , sizeof ( transform ) ) ;
joint + = 16 ;
}
gpu_bundle * bundle = getBundle ( state . animator - > layout ) ;
gpu_bundle_info bundleInfo = { layout , bindings , COUNTOF ( bindings ) } ;
gpu_bundle_write ( & bundle , & bundleInfo , 1 ) ;
uint32_t constants [ ] = { baseVertex , skin - > vertexCount } ;
uint32_t subgroupSize = state . device . subgroupSize ;
gpu_compute_begin ( state . stream ) ;
gpu_bind_pipeline ( state . stream , pipeline , true ) ;
2022-08-09 03:36:22 +00:00
gpu_bind_bundles ( state . stream , shader , & bundle , 0 , 1 , NULL , 0 ) ;
2022-07-04 00:26:31 +00:00
gpu_push_constants ( state . stream , shader , constants , sizeof ( constants ) ) ;
gpu_compute ( state . stream , ( skin - > vertexCount + subgroupSize - 1 ) / subgroupSize , 1 , 1 ) ;
gpu_compute_end ( state . stream ) ;
baseVertex + = skin - > vertexCount ;
}
state . hasReskin = true ;
}
2022-07-13 07:07:15 +00:00
// Readback
Readback * lovrReadbackCreate ( const ReadbackInfo * info ) {
2022-07-14 07:05:58 +00:00
Readback * readback = calloc ( 1 , sizeof ( Readback ) + gpu_sizeof_buffer ( ) ) ;
lovrAssert ( readback , " Out of memory " ) ;
readback - > ref = 1 ;
readback - > tick = state . tick ;
readback - > info = * info ;
readback - > buffer = ( gpu_buffer * ) ( readback + 1 ) ;
2022-07-15 02:23:02 +00:00
switch ( info - > type ) {
case READBACK_BUFFER :
lovrRetain ( info - > buffer . object ) ;
readback - > size = info - > buffer . extent ;
readback - > data = malloc ( readback - > size ) ;
lovrAssert ( readback - > data , " Out of memory " ) ;
2022-07-17 16:50:15 +00:00
readback - > blob = lovrBlobCreate ( readback - > data , readback - > size , " Readback " ) ;
2022-07-15 02:23:02 +00:00
break ;
case READBACK_TEXTURE :
lovrRetain ( info - > texture . object ) ;
TextureFormat format = info - > texture . object - > info . format ;
readback - > size = measureTexture ( format , info - > texture . extent [ 0 ] , info - > texture . extent [ 1 ] , 1 ) ;
readback - > image = lovrImageCreateRaw ( info - > texture . extent [ 0 ] , info - > texture . extent [ 1 ] , format ) ;
break ;
case READBACK_TALLY :
lovrRetain ( info - > tally . object ) ;
2022-07-17 18:38:55 +00:00
uint32_t stride = info - > tally . object - > info . type = = TALLY_SHADER ? 16 : 4 ;
2022-07-15 02:23:02 +00:00
readback - > size = info - > tally . count * stride ;
readback - > data = malloc ( readback - > size ) ;
lovrAssert ( readback - > data , " Out of memory " ) ;
2022-07-17 16:50:15 +00:00
readback - > blob = lovrBlobCreate ( readback - > data , readback - > size , " Readback " ) ;
2022-07-15 02:23:02 +00:00
break ;
2022-07-14 07:05:58 +00:00
}
readback - > pointer = gpu_map ( readback - > buffer , readback - > size , 16 , GPU_MAP_READ ) ;
return readback ;
}
void lovrReadbackDestroy ( void * ref ) {
Readback * readback = ref ;
2022-07-15 02:23:02 +00:00
switch ( readback - > info . type ) {
case READBACK_BUFFER : lovrRelease ( readback - > info . buffer . object , lovrBufferDestroy ) ; break ;
case READBACK_TEXTURE : lovrRelease ( readback - > info . texture . object , lovrTextureDestroy ) ; break ;
case READBACK_TALLY : lovrRelease ( readback - > info . tally . object , lovrTallyDestroy ) ; break ;
}
2022-07-14 07:05:58 +00:00
lovrRelease ( readback - > image , lovrImageDestroy ) ;
2022-07-17 16:50:15 +00:00
lovrRelease ( readback - > blob , lovrBlobDestroy ) ;
2022-07-14 07:05:58 +00:00
free ( readback ) ;
}
2022-07-15 02:23:02 +00:00
const ReadbackInfo * lovrReadbackGetInfo ( Readback * readback ) {
return & readback - > info ;
}
2022-07-14 07:05:58 +00:00
bool lovrReadbackIsComplete ( Readback * readback ) {
return gpu_is_complete ( readback - > tick ) ;
}
bool lovrReadbackWait ( Readback * readback ) {
if ( ( state . tick = = readback - > tick & & state . active ) | | lovrReadbackIsComplete ( readback ) ) {
return false ;
}
bool waited = gpu_wait_tick ( readback - > tick ) ;
if ( waited ) {
processReadbacks ( ) ;
}
return waited ;
}
void * lovrReadbackGetData ( Readback * readback ) {
return lovrReadbackIsComplete ( readback ) ? readback - > data : NULL ;
}
2022-07-13 07:07:15 +00:00
2022-07-17 16:50:15 +00:00
Blob * lovrReadbackGetBlob ( Readback * readback ) {
return lovrReadbackIsComplete ( readback ) ? readback - > blob : NULL ;
}
2022-07-14 07:05:58 +00:00
Image * lovrReadbackGetImage ( Readback * readback ) {
return lovrReadbackIsComplete ( readback ) ? readback - > image : NULL ;
2022-07-13 07:07:15 +00:00
}
2022-06-25 02:00:59 +00:00
// Tally
2022-07-13 07:07:15 +00:00
Tally * lovrTallyCreate ( const TallyInfo * info ) {
2022-06-25 02:00:59 +00:00
lovrCheck ( info - > count > 0 , " Tally count must be greater than zero " ) ;
2022-06-28 06:14:26 +00:00
lovrCheck ( info - > count < = 4096 , " Maximum Tally count is 4096 " ) ;
2022-06-25 02:00:59 +00:00
lovrCheck ( info - > views < = state . limits . renderSize [ 2 ] , " Tally view count can not exceed the maximum view count " ) ;
2022-07-17 18:38:55 +00:00
lovrCheck ( info - > type ! = TALLY_SHADER | | state . features . shaderTally , " This GPU does not support the 'shader' Tally type " ) ;
2022-06-25 02:00:59 +00:00
Tally * tally = calloc ( 1 , sizeof ( Tally ) + gpu_sizeof_tally ( ) ) ;
lovrAssert ( tally , " Out of memory " ) ;
tally - > ref = 1 ;
2022-07-15 02:23:02 +00:00
tally - > tick = state . tick - 1 ;
2022-06-25 02:00:59 +00:00
tally - > info = * info ;
tally - > gpu = ( gpu_tally * ) ( tally + 1 ) ;
2022-07-17 20:17:33 +00:00
uint32_t total = info - > count * ( info - > type = = TALLY_TIME ? 2 * info - > views : 1 ) ;
2022-06-25 02:00:59 +00:00
gpu_tally_init ( tally - > gpu , & ( gpu_tally_info ) {
. type = ( gpu_tally_type ) info - > type ,
. count = total
} ) ;
2022-07-17 20:17:33 +00:00
if ( info - > type = = TALLY_TIME ) {
2022-06-28 06:14:26 +00:00
tally - > buffer = calloc ( 1 , gpu_sizeof_buffer ( ) ) ;
lovrAssert ( tally - > buffer , " Out of memory " ) ;
gpu_buffer_init ( tally - > buffer , & ( gpu_buffer_info ) {
. size = info - > count * 2 * info - > views * sizeof ( uint32_t )
} ) ;
}
2022-06-25 02:00:59 +00:00
return tally ;
}
void lovrTallyDestroy ( void * ref ) {
Tally * tally = ref ;
gpu_tally_destroy ( tally - > gpu ) ;
2022-06-28 06:14:26 +00:00
if ( tally - > buffer ) gpu_buffer_destroy ( tally - > buffer ) ;
free ( tally - > buffer ) ;
2022-06-25 02:00:59 +00:00
free ( tally ) ;
}
2022-07-14 07:05:58 +00:00
const TallyInfo * lovrTallyGetInfo ( Tally * tally ) {
return & tally - > info ;
}
// Tally timestamps aren't very usable in their raw state, since they use unspecified units, aren't
// durations, and when using multiview there's one per view. To make them easier to work with, copy
// them to a temporary buffer, then dispatch a compute shader to subtract pairs and convert to ns,
// writing the final friendly values to a destination Buffer.
static void lovrTallyResolve ( Tally * tally , uint32_t index , uint32_t count , gpu_buffer * buffer , uint32_t offset , gpu_stream * stream ) {
2022-07-15 02:23:02 +00:00
gpu_copy_tally_buffer ( stream , tally - > gpu , tally - > buffer , index , 0 , count * 2 , 4 ) ;
2022-07-14 07:05:58 +00:00
gpu_sync ( stream , & ( gpu_barrier ) {
. prev = GPU_PHASE_TRANSFER ,
. next = GPU_PHASE_SHADER_COMPUTE ,
. flush = GPU_CACHE_TRANSFER_WRITE ,
. clear = GPU_CACHE_STORAGE_READ
} , 1 ) ;
if ( ! state . timeWizard ) {
state . timeWizard = lovrShaderCreate ( & ( ShaderInfo ) {
. type = SHADER_COMPUTE ,
. source [ 0 ] = { lovr_shader_timewizard_comp , sizeof ( lovr_shader_timewizard_comp ) } ,
. label = " timewizard "
} ) ;
}
2022-08-07 01:05:30 +00:00
gpu_pipeline * pipeline = state . pipelines . data [ state . timeWizard - > computePipelineIndex ] ;
2022-07-14 07:05:58 +00:00
gpu_layout * layout = state . layouts . data [ state . timeWizard - > layout ] . gpu ;
gpu_shader * shader = state . timeWizard - > gpu ;
gpu_binding bindings [ ] = {
2022-07-15 02:23:02 +00:00
[ 0 ] = { 0 , GPU_SLOT_STORAGE_BUFFER , . buffer = { tally - > buffer , 0 , count * 2 * tally - > info . views * sizeof ( uint32_t ) } } ,
2022-07-14 07:05:58 +00:00
[ 1 ] = { 1 , GPU_SLOT_STORAGE_BUFFER , . buffer = { buffer , offset , count * sizeof ( uint32_t ) } }
} ;
gpu_bundle * bundle = getBundle ( state . timeWizard - > layout ) ;
gpu_bundle_info bundleInfo = { layout , bindings , COUNTOF ( bindings ) } ;
gpu_bundle_write ( & bundle , & bundleInfo , 1 ) ;
struct { uint32_t first , count , views ; float period ; } constants = {
. first = index ,
. count = count ,
. views = tally - > info . views ,
. period = state . limits . timestampPeriod
} ;
gpu_compute_begin ( stream ) ;
gpu_bind_pipeline ( stream , pipeline , true ) ;
2022-08-09 03:36:22 +00:00
gpu_bind_bundles ( stream , shader , & bundle , 0 , 1 , NULL , 0 ) ;
2022-07-14 07:05:58 +00:00
gpu_push_constants ( stream , shader , & constants , sizeof ( constants ) ) ;
gpu_compute ( stream , ( count + 31 ) / 32 , 1 , 1 ) ;
gpu_compute_end ( stream ) ;
}
2022-04-29 05:30:31 +00:00
// Pass
2022-08-03 05:00:11 +00:00
Pass * lovrGraphicsGetWindowPass ( ) {
return state . windowPass ;
}
Pass * lovrPassCreate ( PassInfo * info ) {
Pass * pass = calloc ( 1 , sizeof ( Pass ) + gpu_sizeof_pass ( ) ) ;
2022-04-29 05:30:31 +00:00
pass - > ref = 1 ;
2022-08-03 05:00:11 +00:00
pass - > gpu = ( gpu_pass * ) ( pass + 1 ) ;
2022-04-29 05:30:31 +00:00
pass - > info = * info ;
2022-06-04 18:54:05 +00:00
2022-08-03 05:00:11 +00:00
arr_init ( & pass - > access , realloc ) ;
arr_init ( & pass - > access , realloc ) ;
2022-06-04 18:54:05 +00:00
2022-08-03 05:00:11 +00:00
if ( info - > type ! = PASS_RENDER ) {
lovrPassReset ( pass ) ;
2022-05-30 19:29:00 +00:00
return pass ;
}
2022-06-04 16:47:02 +00:00
// Validation
2022-05-30 19:29:00 +00:00
Canvas * canvas = & info - > canvas ;
2022-08-03 05:00:11 +00:00
DepthInfo * depth = & canvas - > depth ;
const TextureInfo * t = canvas - > count > 0 ? & canvas - > textures [ 0 ] - > info : & depth - > texture - > info ;
lovrCheck ( canvas - > count > 0 | | depth - > texture , " Render pass must have at least one color or depth texture " ) ;
lovrCheck ( t - > layers < = state . limits . renderSize [ 2 ] , " Pass view count (%d) exceeds the renderSize limit of this GPU (%d) " , t - > layers , state . limits . renderSize [ 2 ] ) ;
2022-05-30 19:29:00 +00:00
lovrCheck ( canvas - > samples = = 1 | | canvas - > samples = = 4 , " Render pass sample count must be 1 or 4...for now " ) ;
2022-08-03 05:00:11 +00:00
pass - > resolve = canvas - > samples > 1 & & t - > samples = = 1 ;
2022-05-30 19:29:00 +00:00
2022-08-03 05:00:11 +00:00
pass - > cameraCount = t - > layers ;
pass - > cameras = malloc ( pass - > cameraCount * sizeof ( Camera ) ) ;
lovrAssert ( pass - > cameras , " Out of memory " ) ;
for ( uint32_t i = 0 ; i < canvas - > count ; i + + ) {
2022-05-30 19:29:00 +00:00
const TextureInfo * texture = & canvas - > textures [ i ] - > info ;
bool renderable = texture - > format = = GPU_FORMAT_SURFACE | | ( state . features . formats [ texture - > format ] & GPU_FEATURE_RENDER ) ;
2022-08-03 05:00:11 +00:00
lovrCheck ( renderable , " This GPU does not support rendering to the texture format used by color target #%d " , i + 1 ) ;
if ( canvas - > samples > 1 & & t - > samples = = 1 ) {
lovrCheck ( canvas - > loads [ i ] ! = LOAD_KEEP , " When multisampling is active, render pass textures must be cleared " ) ;
}
2022-05-30 19:29:00 +00:00
}
2022-08-03 05:00:11 +00:00
if ( depth - > texture | | depth - > format ) {
TextureFormat format = depth - > texture ? depth - > texture - > info . format : depth - > format ;
2022-05-30 19:29:00 +00:00
bool renderable = state . features . formats [ format ] & GPU_FEATURE_RENDER ;
2022-07-17 18:03:00 +00:00
lovrCheck ( format = = FORMAT_D16 | | format = = FORMAT_D32F | | format = = FORMAT_D24S8 | | format = = FORMAT_D32FS8 , " Depth buffer must use a depth format " ) ;
2022-05-30 19:29:00 +00:00
lovrCheck ( renderable , " This GPU does not support depth buffers with this texture format " ) ;
2022-08-03 05:00:11 +00:00
}
// Render Pass
gpu_pass_info passInfo = {
. count = canvas - > count ,
. views = t - > layers ,
. samples = canvas - > samples ,
. resolve = pass - > resolve
} ;
for ( uint32_t i = 0 ; i < canvas - > count ; i + + ) {
TextureInfo * texture = & canvas - > textures [ i ] - > info ;
passInfo . color [ i ] = ( gpu_pass_color_info ) {
. format = ( gpu_texture_format ) texture - > format ,
. srgb = texture - > srgb ,
. usage = convertTextureUsage ( texture - > usage ) ,
. load = ( gpu_load_op ) canvas - > loads [ i ] ,
. save = ( gpu_save_op ) GPU_SAVE_OP_KEEP
} ;
if ( pass - > resolve ) {
passInfo . color [ i ] . resolveUsage = passInfo . color [ i ] . usage ;
passInfo . color [ i ] . usage = GPU_TEXTURE_RENDER | GPU_TEXTURE_TRANSIENT ;
}
}
if ( depth - > texture | | depth - > format ) {
passInfo . depth = ( gpu_pass_depth_info ) {
. format = ( gpu_texture_format ) ( depth - > texture ? depth - > texture - > info . format : depth - > format ) ,
. usage = depth - > texture ? convertTextureUsage ( depth - > texture - > info . usage ) : GPU_TEXTURE_TRANSIENT ,
. load = ( gpu_load_op ) depth - > load ,
. save = depth - > texture ? GPU_SAVE_OP_KEEP : GPU_SAVE_OP_DISCARD
} ;
}
gpu_pass_init ( pass - > gpu , & passInfo ) ;
pass - > target . pass = pass - > gpu ;
lovrPassSetTarget ( pass , canvas - > textures , canvas - > depth . texture ) ;
lovrPassSetClear ( pass , canvas - > clears , canvas - > depth . clear , 0 ) ;
lovrPassReset ( pass ) ;
return pass ;
}
static void lovrPassRelease ( Pass * pass ) {
for ( size_t i = 0 ; i < pass - > access . length ; i + + ) {
Access * access = & pass - > access . data [ i ] ;
lovrRelease ( access - > buffer , lovrBufferDestroy ) ;
lovrRelease ( access - > texture , lovrTextureDestroy ) ;
}
if ( pass - > info . type = = PASS_RENDER ) {
for ( size_t i = 0 ; i < = pass - > pipelineIndex ; i + + ) {
lovrRelease ( pass - > pipelines [ i ] . font , lovrFontDestroy ) ;
lovrRelease ( pass - > pipelines [ i ] . sampler , lovrSamplerDestroy ) ;
lovrRelease ( pass - > pipelines [ i ] . shader , lovrShaderDestroy ) ;
lovrRelease ( pass - > pipelines [ i ] . material , lovrMaterialDestroy ) ;
pass - > pipelines [ i ] . font = NULL ;
pass - > pipelines [ i ] . sampler = NULL ;
pass - > pipelines [ i ] . shader = NULL ;
pass - > pipelines [ i ] . material = NULL ;
}
}
}
void lovrPassDestroy ( void * ref ) {
Pass * pass = ref ;
lovrPassRelease ( pass ) ;
gpu_pass_destroy ( pass - > gpu ) ;
arr_free ( & pass - > access ) ;
for ( uint32_t i = 0 ; i < pass - > info . canvas . count ; i + + ) {
lovrRelease ( pass - > color [ i ] , lovrTextureDestroy ) ;
if ( pass - > resolve & & pass - > target . color [ i ] . texture ) {
gpu_texture_destroy ( pass - > target . color [ i ] . texture ) ;
free ( pass - > target . color [ i ] . texture ) ;
2022-05-11 19:51:13 +00:00
}
2022-05-30 19:29:00 +00:00
}
2022-05-11 19:51:13 +00:00
2022-08-03 05:00:11 +00:00
lovrRelease ( pass - > depth , lovrTextureDestroy ) ;
2022-06-04 16:47:02 +00:00
2022-08-03 05:00:11 +00:00
if ( pass - > info . canvas . depth . format & & pass - > target . depth . texture ) {
gpu_texture_destroy ( pass - > target . depth . texture ) ;
free ( pass - > target . depth . texture ) ;
}
free ( pass - > cameras ) ;
free ( pass ) ;
}
const PassInfo * lovrPassGetInfo ( Pass * pass ) {
return & pass - > info ;
}
uint32_t lovrPassGetWidth ( Pass * pass ) {
if ( pass - > info . type ! = PASS_RENDER ) {
return 0 ;
} else {
return ( pass - > color [ 0 ] ? pass - > color [ 0 ] : pass - > depth ) - > info . width ;
}
}
uint32_t lovrPassGetHeight ( Pass * pass ) {
if ( pass - > info . type ! = PASS_RENDER ) {
return 0 ;
} else {
return ( pass - > color [ 0 ] ? pass - > color [ 0 ] : pass - > depth ) - > info . height ;
}
}
uint32_t lovrPassGetViewCount ( Pass * pass ) {
return pass - > cameraCount ;
}
uint32_t lovrPassGetSampleCount ( Pass * pass ) {
return pass - > info . canvas . samples ;
}
void lovrPassGetTarget ( Pass * pass , Texture * color [ 4 ] , Texture * * depth ) {
memcpy ( color , pass - > color , pass - > info . canvas . count * sizeof ( Texture ) ) ;
* depth = pass - > depth ;
}
void lovrPassSetTarget ( Pass * pass , Texture * color [ 4 ] , Texture * depth ) {
const TextureInfo * t = color [ 0 ] ? & color [ 0 ] - > info : & depth - > info ;
bool resized = pass - > target . size [ 0 ] ! = t - > width | | pass - > target . size [ 1 ] ! = t - > height ;
if ( resized ) {
lovrCheck ( t - > width < = state . limits . renderSize [ 0 ] , " Texture width (%d) exceeds the renderSize limit of this GPU (%d) " , t - > width , state . limits . renderSize [ 0 ] ) ;
lovrCheck ( t - > height < = state . limits . renderSize [ 1 ] , " Texture height (%d) exceeds the renderSize limit of this GPU (%d) " , t - > height , state . limits . renderSize [ 1 ] ) ;
pass - > target . size [ 0 ] = t - > width ;
pass - > target . size [ 1 ] = t - > height ;
}
Canvas * canvas = & pass - > info . canvas ;
gpu_texture_info tempAttachmentInfo = {
. type = GPU_TEXTURE_ARRAY ,
. size [ 0 ] = t - > width ,
. size [ 1 ] = t - > height ,
. size [ 2 ] = t - > layers ,
. mipmaps = 1 ,
. samples = canvas - > samples ,
. usage = GPU_TEXTURE_RENDER | GPU_TEXTURE_TRANSIENT
2022-05-30 19:29:00 +00:00
} ;
2022-08-03 05:00:11 +00:00
for ( uint32_t i = 0 ; i < canvas - > count ; i + + ) {
lovrRetain ( color [ i ] ) ;
lovrRelease ( pass - > color [ i ] , lovrTextureDestroy ) ;
lovrCheck ( color [ i ] , " Color target #%d is missing " , i + 1 ) ;
const TextureInfo * info = & color [ i ] - > info ;
lovrCheck ( info - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( info - > width = = t - > width , " Texture sizes must match " ) ;
lovrCheck ( info - > height = = t - > height , " Texture sizes must match " ) ;
lovrCheck ( info - > layers = = t - > layers , " Texture sizes must match " ) ;
if ( pass - > color [ 0 ] ) {
lovrCheck ( info - > samples = = pass - > color [ 0 ] - > info . samples , " Color target sample count is different than the sample count of the texture used to create the Pass " ) ;
2022-05-11 19:51:13 +00:00
}
2022-08-03 05:00:11 +00:00
if ( pass - > resolve ) {
if ( resized ) {
if ( pass - > target . color [ i ] . texture ) {
gpu_texture_destroy ( pass - > target . color [ i ] . texture ) ;
} else {
pass - > target . color [ i ] . texture = malloc ( gpu_sizeof_texture ( ) ) ;
lovrAssert ( pass - > target . color [ i ] . texture , " Out of memory " ) ;
}
tempAttachmentInfo . format = info - > format ;
tempAttachmentInfo . srgb = info - > srgb ;
gpu_texture_init ( pass - > target . color [ i ] . texture , & tempAttachmentInfo ) ;
}
2022-06-04 21:28:23 +00:00
2022-08-03 05:00:11 +00:00
pass - > target . color [ i ] . resolve = color [ i ] - > renderView ;
2022-06-23 02:05:36 +00:00
} else {
2022-08-03 05:00:11 +00:00
pass - > target . color [ i ] . texture = color [ i ] - > renderView ;
2022-06-23 02:05:36 +00:00
}
2022-08-03 05:00:11 +00:00
pass - > color [ i ] = color [ i ] ;
2022-05-30 19:29:00 +00:00
}
2022-05-11 19:51:13 +00:00
2022-05-30 19:29:00 +00:00
if ( canvas - > depth . texture ) {
2022-08-03 05:00:11 +00:00
lovrRetain ( depth ) ;
lovrRelease ( pass - > depth , lovrTextureDestroy ) ;
lovrCheck ( depth , " Depth target is missing " ) ;
const TextureInfo * info = & depth - > info ;
lovrCheck ( info - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( info - > width = = t - > width , " Texture sizes must match " ) ;
lovrCheck ( info - > height = = t - > height , " Texture sizes must match " ) ;
lovrCheck ( info - > layers = = t - > layers , " Texture sizes must match " ) ;
lovrCheck ( info - > samples = = canvas - > samples , " Sorry, resolving depth textures is not supported yet " ) ;
pass - > target . depth . texture = depth - > renderView ;
pass - > depth = depth ;
} else if ( canvas - > depth . format & & resized ) {
if ( pass - > target . depth . texture ) {
gpu_texture_destroy ( pass - > target . depth . texture ) ;
2022-06-23 02:05:36 +00:00
} else {
2022-08-03 05:00:11 +00:00
pass - > target . depth . texture = malloc ( gpu_sizeof_texture ( ) ) ;
lovrAssert ( pass - > target . depth . texture , " Out of memory " ) ;
2022-06-23 02:05:36 +00:00
}
2022-05-11 19:51:13 +00:00
2022-08-03 05:00:11 +00:00
tempAttachmentInfo . format = canvas - > depth . format ;
tempAttachmentInfo . srgb = false ;
gpu_texture_init ( pass - > target . depth . texture , & tempAttachmentInfo ) ;
2022-06-04 08:33:11 +00:00
}
2022-08-03 05:00:11 +00:00
}
2022-05-11 19:51:13 +00:00
2022-08-03 05:00:11 +00:00
void lovrPassGetClear ( Pass * pass , float color [ 4 ] [ 4 ] , float * depth , uint8_t * stencil ) {
for ( uint32_t i = 0 ; i < pass - > info . canvas . count ; i + + ) {
color [ i ] [ 0 ] = lovrMathLinearToGamma ( pass - > target . color [ i ] . clear [ 0 ] ) ;
color [ i ] [ 1 ] = lovrMathLinearToGamma ( pass - > target . color [ i ] . clear [ 1 ] ) ;
color [ i ] [ 2 ] = lovrMathLinearToGamma ( pass - > target . color [ i ] . clear [ 2 ] ) ;
color [ i ] [ 3 ] = pass - > target . color [ i ] . clear [ 3 ] ;
}
* depth = pass - > target . depth . clear . depth ;
* stencil = pass - > target . depth . clear . stencil ;
}
2022-05-11 19:51:13 +00:00
2022-08-03 05:00:11 +00:00
void lovrPassSetClear ( Pass * pass , float color [ 4 ] [ 4 ] , float depth , uint8_t stencil ) {
for ( uint32_t i = 0 ; i < pass - > info . canvas . count ; i + + ) {
pass - > target . color [ i ] . clear [ 0 ] = lovrMathGammaToLinear ( color [ i ] [ 0 ] ) ;
pass - > target . color [ i ] . clear [ 1 ] = lovrMathGammaToLinear ( color [ i ] [ 1 ] ) ;
pass - > target . color [ i ] . clear [ 2 ] = lovrMathGammaToLinear ( color [ i ] [ 2 ] ) ;
pass - > target . color [ i ] . clear [ 3 ] = color [ i ] [ 3 ] ;
}
pass - > target . depth . clear . depth = depth ;
pass - > target . depth . clear . stencil = stencil ;
}
2022-06-01 04:20:01 +00:00
2022-08-03 05:00:11 +00:00
void lovrPassReset ( Pass * pass ) {
lovrPassRelease ( pass ) ;
2022-06-04 16:47:02 +00:00
2022-08-03 05:00:11 +00:00
beginFrame ( ) ;
pass - > stream = gpu_stream_begin ( ) ;
pass - > tick = state . tick ;
arr_clear ( & pass - > access ) ;
2022-06-04 16:47:02 +00:00
2022-08-03 05:00:11 +00:00
pass - > transformIndex = 0 ;
pass - > transform = pass - > transforms [ pass - > transformIndex ] ;
2022-05-30 19:29:00 +00:00
mat4_identity ( pass - > transform ) ;
2022-07-06 04:58:54 +00:00
pass - > pipelineIndex = 0 ;
2022-08-03 05:00:11 +00:00
pass - > pipeline = & pass - > pipelines [ pass - > pipelineIndex ] ;
pass - > pipeline - > dirty = true ;
float color [ 4 ] = { 1.f , 1.f , 1.f , 1.f } ;
float viewport [ 4 ] = { 0.f , 0.f , ( float ) pass - > target . size [ 0 ] , ( float ) pass - > target . size [ 1 ] } ;
float depthRange [ 2 ] = { 0.f , 1.f } ;
uint32_t scissor [ 4 ] = { 0 , 0 , pass - > target . size [ 0 ] , pass - > target . size [ 1 ] } ;
pass - > pipeline - > mode = MESH_TRIANGLES ;
memcpy ( pass - > pipeline - > color , color , sizeof ( color ) ) ;
memcpy ( pass - > pipeline - > viewport , viewport , sizeof ( viewport ) ) ;
memcpy ( pass - > pipeline - > depthRange , depthRange , sizeof ( depthRange ) ) ;
memcpy ( pass - > pipeline - > scissor , scissor , sizeof ( scissor ) ) ;
pass - > pipeline - > formatHash = 0 ;
2022-06-04 16:47:02 +00:00
pass - > pipeline - > info = ( gpu_pipeline_info ) {
2022-08-03 05:00:11 +00:00
. pass = pass - > gpu ,
. colorCount = pass - > info . canvas . count ,
. multisample . count = pass - > info . canvas . samples ,
. viewCount = pass - > cameraCount ,
2022-07-17 19:37:59 +00:00
. depth . test = GPU_COMPARE_GEQUAL ,
2022-06-04 16:47:02 +00:00
. depth . write = true
} ;
2022-05-30 22:36:31 +00:00
2022-08-03 05:00:11 +00:00
for ( uint32_t i = 0 ; i < pass - > info . canvas . count ; i + + ) {
2022-06-04 16:47:02 +00:00
pass - > pipeline - > info . color [ i ] . mask = 0xf ;
2022-05-30 22:36:31 +00:00
}
2022-08-03 05:00:11 +00:00
pass - > pipeline - > material = NULL ;
pass - > pipeline - > sampler = NULL ;
2022-05-30 19:29:00 +00:00
pass - > pipeline - > shader = NULL ;
2022-08-03 05:00:11 +00:00
pass - > pipeline - > font = NULL ;
2022-06-17 06:49:09 +00:00
pass - > materialDirty = true ;
2022-08-03 05:00:11 +00:00
pass - > samplerDirty = true ;
2022-05-30 19:29:00 +00:00
2022-05-30 22:36:31 +00:00
memset ( pass - > constants , 0 , sizeof ( pass - > constants ) ) ;
pass - > constantsDirty = true ;
2022-05-30 19:29:00 +00:00
pass - > bindingMask = 0 ;
pass - > bindingsDirty = true ;
2022-08-03 05:00:11 +00:00
float aspect = ( float ) pass - > target . size [ 0 ] / pass - > target . size [ 1 ] ;
2022-05-30 22:36:31 +00:00
for ( uint32_t i = 0 ; i < pass - > cameraCount ; i + + ) {
mat4_identity ( pass - > cameras [ i ] . view ) ;
2022-08-03 05:00:11 +00:00
mat4_perspective ( pass - > cameras [ i ] . projection , 1.f , aspect , .01f , 0.f ) ;
2022-05-30 22:36:31 +00:00
}
2022-05-30 19:29:00 +00:00
pass - > cameraDirty = true ;
2022-05-24 06:10:11 +00:00
2022-08-06 20:37:27 +00:00
gpu_buffer_binding globals = { tempAlloc ( gpu_sizeof_buffer ( ) ) , 0 , sizeof ( Globals ) } ;
2022-05-30 22:36:31 +00:00
gpu_buffer_binding cameras = { tempAlloc ( gpu_sizeof_buffer ( ) ) , 0 , pass - > cameraCount * sizeof ( Camera ) } ;
gpu_buffer_binding draws = { tempAlloc ( gpu_sizeof_buffer ( ) ) , 0 , 256 * sizeof ( DrawData ) } ;
2022-08-03 05:00:11 +00:00
pass - > drawCount = 0 ;
2022-05-30 22:36:31 +00:00
2022-08-06 20:37:27 +00:00
pass - > builtins [ 0 ] = ( gpu_binding ) { 0 , GPU_SLOT_UNIFORM_BUFFER , . buffer = globals } ;
pass - > builtins [ 1 ] = ( gpu_binding ) { 1 , GPU_SLOT_UNIFORM_BUFFER , . buffer = cameras } ;
pass - > builtins [ 2 ] = ( gpu_binding ) { 2 , GPU_SLOT_UNIFORM_BUFFER , . buffer = draws } ;
pass - > builtins [ 3 ] = ( gpu_binding ) { 3 , GPU_SLOT_SAMPLER , . sampler = NULL } ;
Globals * global = gpu_map ( pass - > builtins [ 0 ] . buffer . object , sizeof ( Globals ) , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
global - > resolution [ 0 ] = pass - > target . size [ 0 ] ;
global - > resolution [ 1 ] = pass - > target . size [ 1 ] ;
global - > resolution [ 2 ] = 1.f / pass - > target . size [ 0 ] ;
global - > resolution [ 3 ] = 1.f / pass - > target . size [ 1 ] ;
# ifndef LOVR_DISABLE_HEADSET
global - > time = lovrHeadsetInterface ? lovrHeadsetInterface - > getDisplayTime ( ) : os_get_time ( ) ;
# else
global - > time = os_get_time ( ) ;
# endif
2022-05-30 22:36:31 +00:00
pass - > vertexBuffer = NULL ;
pass - > indexBuffer = NULL ;
2022-07-12 03:52:35 +00:00
memset ( pass - > shapeCache , 0 , sizeof ( pass - > shapeCache ) ) ;
2022-08-03 05:00:11 +00:00
if ( pass - > info . type = = PASS_RENDER ) {
Canvas * canvas = & pass - > info . canvas ;
2022-06-06 19:38:15 +00:00
2022-08-03 05:00:11 +00:00
for ( uint32_t i = 0 ; i < canvas - > count ; i + + ) {
if ( pass - > color [ i ] = = state . window ) {
if ( pass - > resolve ) { // Make sure window swapchain is updated
pass - > target . color [ i ] . resolve = lovrGraphicsGetWindowTexture ( ) - > gpu ;
} else {
pass - > target . color [ i ] . texture = lovrGraphicsGetWindowTexture ( ) - > gpu ;
}
2022-08-06 04:05:02 +00:00
// The window pass always uses the global clear color. The intent is that exposing a global
// clear color that applies to the window/headset is more friendly than messing with passes.
2022-08-06 05:14:09 +00:00
memcpy ( pass - > target . color [ 0 ] . clear , state . background , 4 * sizeof ( float ) ) ;
2022-08-03 05:00:11 +00:00
}
2022-06-06 19:38:15 +00:00
2022-08-03 05:00:11 +00:00
if ( canvas - > mipmap ) {
trackTexture ( pass , pass - > color [ i ] , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
} else {
gpu_cache cache = GPU_CACHE_COLOR_WRITE | ( canvas - > loads [ i ] = = LOAD_KEEP ? GPU_CACHE_COLOR_READ : 0 ) ;
trackTexture ( pass , pass - > color [ i ] , GPU_PHASE_COLOR , cache ) ;
}
}
2022-04-29 05:30:31 +00:00
2022-08-03 05:00:11 +00:00
if ( pass - > depth ) {
if ( canvas - > mipmap & & pass - > depth - > info . mipmaps > 1 ) {
trackTexture ( pass , pass - > depth , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
} else {
gpu_phase phase = canvas - > depth . load = = LOAD_KEEP ? GPU_PHASE_DEPTH_EARLY : GPU_PHASE_DEPTH_LATE ;
gpu_cache cache = GPU_CACHE_DEPTH_WRITE | ( canvas - > depth . load = = LOAD_KEEP ? GPU_CACHE_DEPTH_READ : 0 ) ;
trackTexture ( pass , pass - > depth , phase , cache ) ;
}
}
2022-04-29 05:30:31 +00:00
2022-08-03 05:00:11 +00:00
gpu_render_begin ( pass - > stream , & pass - > target ) ;
lovrPassSetViewport ( pass , pass - > pipeline - > viewport , pass - > pipeline - > depthRange ) ;
lovrPassSetScissor ( pass , pass - > pipeline - > scissor ) ;
// The default vertex buffer is always in the first slot, used for default attribute values
gpu_buffer * buffers [ ] = { state . defaultBuffer - > gpu , state . defaultBuffer - > gpu } ;
gpu_bind_vertex_buffers ( pass - > stream , buffers , NULL , 0 , 2 ) ;
} else if ( pass - > info . type = = PASS_COMPUTE ) {
gpu_compute_begin ( pass - > stream ) ;
}
2022-04-29 05:30:31 +00:00
}
2022-05-30 19:29:00 +00:00
void lovrPassGetViewMatrix ( Pass * pass , uint32_t index , float * viewMatrix ) {
2022-07-30 22:20:01 +00:00
lovrCheck ( index < pass - > cameraCount , " Invalid view index '%d' " , index ) ;
2022-05-30 19:29:00 +00:00
mat4_init ( viewMatrix , pass - > cameras [ index ] . view ) ;
}
void lovrPassSetViewMatrix ( Pass * pass , uint32_t index , float * viewMatrix ) {
2022-07-30 22:20:01 +00:00
lovrCheck ( index < pass - > cameraCount , " Invalid view index '%d' " , index ) ;
2022-05-30 19:29:00 +00:00
mat4_init ( pass - > cameras [ index ] . view , viewMatrix ) ;
pass - > cameraDirty = true ;
}
void lovrPassGetProjection ( Pass * pass , uint32_t index , float * projection ) {
2022-07-30 22:20:01 +00:00
lovrCheck ( index < pass - > cameraCount , " Invalid view index '%d' " , index ) ;
2022-05-30 19:29:00 +00:00
mat4_init ( projection , pass - > cameras [ index ] . projection ) ;
}
void lovrPassSetProjection ( Pass * pass , uint32_t index , float * projection ) {
2022-07-30 22:20:01 +00:00
lovrCheck ( index < pass - > cameraCount , " Invalid view index '%d' " , index ) ;
2022-05-30 19:29:00 +00:00
mat4_init ( pass - > cameras [ index ] . projection , projection ) ;
pass - > cameraDirty = true ;
2022-07-17 15:59:39 +00:00
// If the handedness of the projection changes, flip the winding
if ( index = = 0 & & ( projection [ 5 ] > 0.f ! = pass - > cameras [ 0 ] . projection [ 5 ] > 0.f ) ) {
pass - > pipeline - > info . rasterizer . winding = ! pass - > pipeline - > info . rasterizer . winding ;
pass - > pipeline - > dirty = true ;
}
2022-05-30 19:29:00 +00:00
}
2022-05-07 00:26:38 +00:00
void lovrPassPush ( Pass * pass , StackType stack ) {
2022-06-01 04:57:55 +00:00
switch ( stack ) {
case STACK_TRANSFORM :
pass - > transform = pass - > transforms [ + + pass - > transformIndex ] ;
lovrCheck ( pass - > transformIndex < COUNTOF ( pass - > transforms ) , " %s stack overflow (more pushes than pops?) " , " Transform " ) ;
mat4_init ( pass - > transforms [ pass - > transformIndex ] , pass - > transforms [ pass - > transformIndex - 1 ] ) ;
break ;
2022-07-30 22:03:54 +00:00
case STACK_STATE :
2022-06-01 04:57:55 +00:00
pass - > pipeline = & pass - > pipelines [ + + pass - > pipelineIndex ] ;
lovrCheck ( pass - > pipelineIndex < COUNTOF ( pass - > pipelines ) , " %s stack overflow (more pushes than pops?) " , " Pipeline " ) ;
memcpy ( pass - > pipeline , & pass - > pipelines [ pass - > pipelineIndex - 1 ] , sizeof ( Pipeline ) ) ;
2022-07-18 02:53:31 +00:00
lovrRetain ( pass - > pipeline - > font ) ;
2022-06-08 03:42:10 +00:00
lovrRetain ( pass - > pipeline - > sampler ) ;
2022-06-18 00:43:26 +00:00
lovrRetain ( pass - > pipeline - > shader ) ;
lovrRetain ( pass - > pipeline - > material ) ;
2022-06-01 04:57:55 +00:00
break ;
default : break ;
2022-05-07 00:26:38 +00:00
}
}
void lovrPassPop ( Pass * pass , StackType stack ) {
2022-06-01 04:57:55 +00:00
switch ( stack ) {
case STACK_TRANSFORM :
pass - > transform = pass - > transforms [ - - pass - > transformIndex ] ;
lovrCheck ( pass - > transformIndex < COUNTOF ( pass - > transforms ) , " %s stack underflow (more pops than pushes?) " , " Transform " ) ;
break ;
2022-07-30 22:03:54 +00:00
case STACK_STATE :
2022-07-18 02:53:31 +00:00
lovrRelease ( pass - > pipeline - > font , lovrFontDestroy ) ;
2022-06-18 00:43:26 +00:00
lovrRelease ( pass - > pipeline - > sampler , lovrSamplerDestroy ) ;
2022-06-01 04:57:55 +00:00
lovrRelease ( pass - > pipeline - > shader , lovrShaderDestroy ) ;
2022-06-18 00:43:26 +00:00
lovrRelease ( pass - > pipeline - > material , lovrMaterialDestroy ) ;
2022-06-01 04:57:55 +00:00
pass - > pipeline = & pass - > pipelines [ - - pass - > pipelineIndex ] ;
lovrCheck ( pass - > pipelineIndex < COUNTOF ( pass - > pipelines ) , " %s stack underflow (more pops than pushes?) " , " Pipeline " ) ;
2022-07-30 22:20:01 +00:00
lovrPassSetViewport ( pass , pass - > pipeline - > viewport , pass - > pipeline - > depthRange ) ;
lovrPassSetScissor ( pass , pass - > pipeline - > scissor ) ;
2022-06-08 03:42:10 +00:00
pass - > pipeline - > dirty = true ;
pass - > samplerDirty = true ;
2022-06-17 06:49:09 +00:00
pass - > materialDirty = true ;
2022-06-01 04:57:55 +00:00
break ;
default : break ;
2022-05-07 00:26:38 +00:00
}
}
void lovrPassOrigin ( Pass * pass ) {
mat4_identity ( pass - > transform ) ;
}
void lovrPassTranslate ( Pass * pass , vec3 translation ) {
mat4_translate ( pass - > transform , translation [ 0 ] , translation [ 1 ] , translation [ 2 ] ) ;
}
void lovrPassRotate ( Pass * pass , quat rotation ) {
mat4_rotateQuat ( pass - > transform , rotation ) ;
}
void lovrPassScale ( Pass * pass , vec3 scale ) {
mat4_scale ( pass - > transform , scale [ 0 ] , scale [ 1 ] , scale [ 2 ] ) ;
}
void lovrPassTransform ( Pass * pass , mat4 transform ) {
mat4_mul ( pass - > transform , transform ) ;
}
2022-05-11 19:50:26 +00:00
void lovrPassSetAlphaToCoverage ( Pass * pass , bool enabled ) {
pass - > pipeline - > dirty | = enabled ! = pass - > pipeline - > info . multisample . alphaToCoverage ;
pass - > pipeline - > info . multisample . alphaToCoverage = enabled ;
}
void lovrPassSetBlendMode ( Pass * pass , BlendMode mode , BlendAlphaMode alphaMode ) {
if ( mode = = BLEND_NONE ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . color [ 0 ] . blend . enabled ;
memset ( & pass - > pipeline - > info . color [ 0 ] . blend , 0 , sizeof ( gpu_blend_state ) ) ;
return ;
}
gpu_blend_state * blend = & pass - > pipeline - > info . color [ 0 ] . blend ;
switch ( mode ) {
case BLEND_ALPHA :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE_MINUS_SRC_ALPHA ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ONE_MINUS_SRC_ALPHA ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_ADD :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ZERO ;
blend - > alpha . dst = GPU_BLEND_ONE ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_SUBTRACT :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE ;
blend - > color . op = GPU_BLEND_RSUB ;
blend - > alpha . src = GPU_BLEND_ZERO ;
blend - > alpha . dst = GPU_BLEND_ONE ;
blend - > alpha . op = GPU_BLEND_RSUB ;
break ;
case BLEND_MULTIPLY :
blend - > color . src = GPU_BLEND_DST_COLOR ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_DST_COLOR ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_LIGHTEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_MAX ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_MAX ;
break ;
case BLEND_DARKEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_MIN ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_MIN ;
break ;
case BLEND_SCREEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE_MINUS_SRC_COLOR ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ONE_MINUS_SRC_COLOR ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
default : lovrUnreachable ( ) ;
} ;
if ( alphaMode = = BLEND_PREMULTIPLIED & & mode ! = BLEND_MULTIPLY ) {
blend - > color . src = GPU_BLEND_ONE ;
}
blend - > enabled = true ;
pass - > pipeline - > dirty = true ;
}
2022-05-12 00:30:08 +00:00
void lovrPassSetColor ( Pass * pass , float color [ 4 ] ) {
pass - > pipeline - > color [ 0 ] = lovrMathGammaToLinear ( color [ 0 ] ) ;
pass - > pipeline - > color [ 1 ] = lovrMathGammaToLinear ( color [ 1 ] ) ;
pass - > pipeline - > color [ 2 ] = lovrMathGammaToLinear ( color [ 2 ] ) ;
pass - > pipeline - > color [ 3 ] = color [ 3 ] ;
}
void lovrPassSetColorWrite ( Pass * pass , bool r , bool g , bool b , bool a ) {
2022-05-11 19:50:26 +00:00
uint8_t mask = ( r < < 0 ) | ( g < < 1 ) | ( b < < 2 ) | ( a < < 3 ) ;
pass - > pipeline - > dirty | = pass - > pipeline - > info . color [ 0 ] . mask ! = mask ;
pass - > pipeline - > info . color [ 0 ] . mask = mask ;
}
void lovrPassSetCullMode ( Pass * pass , CullMode mode ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . cullMode ! = ( gpu_cull_mode ) mode ;
pass - > pipeline - > info . rasterizer . cullMode = ( gpu_cull_mode ) mode ;
}
void lovrPassSetDepthTest ( Pass * pass , CompareMode test ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . depth . test ! = ( gpu_compare_mode ) test ;
pass - > pipeline - > info . depth . test = ( gpu_compare_mode ) test ;
}
void lovrPassSetDepthWrite ( Pass * pass , bool write ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . depth . write ! = write ;
pass - > pipeline - > info . depth . write = write ;
}
void lovrPassSetDepthOffset ( Pass * pass , float offset , float sloped ) {
pass - > pipeline - > info . rasterizer . depthOffset = offset ;
pass - > pipeline - > info . rasterizer . depthOffsetSloped = sloped ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetDepthClamp ( Pass * pass , bool clamp ) {
if ( state . features . depthClamp ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . depthClamp ! = clamp ;
pass - > pipeline - > info . rasterizer . depthClamp = clamp ;
}
}
2022-07-18 02:53:31 +00:00
void lovrPassSetFont ( Pass * pass , Font * font ) {
if ( pass - > pipeline - > font ! = font ) {
lovrRetain ( font ) ;
lovrRelease ( pass - > pipeline - > font , lovrFontDestroy ) ;
pass - > pipeline - > font = font ;
}
}
2022-06-25 02:38:45 +00:00
void lovrPassSetMaterial ( Pass * pass , Material * material , Texture * texture ) {
if ( texture ) {
material = lovrTextureGetMaterial ( texture ) ;
}
2022-06-17 06:49:09 +00:00
material = material ? material : state . defaultMaterial ;
if ( pass - > pipeline - > material ! = material ) {
lovrRetain ( material ) ;
lovrRelease ( pass - > pipeline - > material , lovrMaterialDestroy ) ;
pass - > pipeline - > material = material ;
pass - > materialDirty = true ;
}
2022-06-10 06:05:32 +00:00
}
2022-07-13 02:35:23 +00:00
void lovrPassSetMeshMode ( Pass * pass , MeshMode mode ) {
pass - > pipeline - > mode = mode ;
}
2022-06-08 03:42:10 +00:00
void lovrPassSetSampler ( Pass * pass , Sampler * sampler ) {
if ( sampler ! = pass - > pipeline - > sampler ) {
lovrRetain ( sampler ) ;
lovrRelease ( pass - > pipeline - > sampler , lovrSamplerDestroy ) ;
pass - > pipeline - > sampler = sampler ;
pass - > samplerDirty = true ;
}
}
2022-06-06 19:38:15 +00:00
void lovrPassSetScissor ( Pass * pass , uint32_t scissor [ 4 ] ) {
2022-07-30 22:20:01 +00:00
if ( pass - > info . type = = PASS_RENDER ) gpu_set_scissor ( pass - > stream , scissor ) ;
2022-06-06 19:38:15 +00:00
memcpy ( pass - > pipeline - > scissor , scissor , 4 * sizeof ( uint32_t ) ) ;
}
2022-05-11 19:50:26 +00:00
void lovrPassSetShader ( Pass * pass , Shader * shader ) {
2022-05-24 05:32:36 +00:00
Shader * previous = pass - > pipeline - > shader ;
if ( shader = = previous ) return ;
// Clear any bindings for resources that share the same slot but have different types
2022-06-28 06:15:19 +00:00
if ( shader ) {
if ( previous ) {
for ( uint32_t i = 0 , j = 0 ; i < previous - > resourceCount & & j < shader - > resourceCount ; ) {
if ( previous - > resources [ i ] . binding < shader - > resources [ j ] . binding ) {
i + + ;
} else if ( previous - > resources [ i ] . binding > shader - > resources [ j ] . binding ) {
j + + ;
} else {
if ( previous - > resources [ i ] . type ! = shader - > resources [ j ] . type ) {
2022-07-04 00:26:31 +00:00
pass - > bindingMask & = ~ ( 1u < < shader - > resources [ j ] . binding ) ;
2022-06-28 06:15:19 +00:00
}
i + + ;
j + + ;
2022-05-24 05:32:36 +00:00
}
}
}
2022-06-28 06:15:19 +00:00
uint32_t shaderSlots = ( shader - > bufferMask | shader - > textureMask | shader - > samplerMask ) ;
uint32_t missingResources = shaderSlots & ~ pass - > bindingMask ;
2022-05-24 05:32:36 +00:00
2022-06-28 06:15:19 +00:00
// Assign default bindings to any slots used by the shader that are missing resources
if ( missingResources ) {
for ( uint32_t i = 0 ; i < 32 ; i + + ) { // TODO biterationtrinsics
uint32_t bit = ( 1u < < i ) ;
2022-05-24 05:32:36 +00:00
2022-06-28 06:15:19 +00:00
if ( ~ missingResources & bit ) {
continue ;
}
2022-05-24 05:32:36 +00:00
2022-06-28 06:15:19 +00:00
pass - > bindings [ i ] . number = i ;
pass - > bindings [ i ] . type = shader - > resources [ i ] . type ;
if ( shader - > bufferMask & bit ) {
pass - > bindings [ i ] . buffer . object = state . defaultBuffer - > gpu ;
pass - > bindings [ i ] . buffer . offset = 0 ;
pass - > bindings [ i ] . buffer . extent = state . defaultBuffer - > size ;
} else if ( shader - > textureMask & bit ) {
pass - > bindings [ i ] . texture = state . defaultTexture - > gpu ;
} else if ( shader - > samplerMask & bit ) {
pass - > bindings [ i ] . sampler = state . defaultSamplers [ FILTER_LINEAR ] - > gpu ;
}
pass - > bindingMask | = bit ;
2022-05-24 05:32:36 +00:00
}
2022-06-28 06:15:19 +00:00
pass - > bindingsDirty = true ;
2022-05-24 05:32:36 +00:00
}
2022-06-28 06:15:19 +00:00
pass - > pipeline - > info . shader = shader - > gpu ;
pass - > pipeline - > info . flags = shader - > flags ;
pass - > pipeline - > info . flagCount = shader - > overrideCount ;
2022-05-24 05:32:36 +00:00
}
2022-05-11 19:50:26 +00:00
lovrRetain ( shader ) ;
2022-05-24 05:32:36 +00:00
lovrRelease ( previous , lovrShaderDestroy ) ;
2022-05-11 19:50:26 +00:00
pass - > pipeline - > shader = shader ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetStencilTest ( Pass * pass , CompareMode test , uint8_t value , uint8_t mask ) {
bool hasReplace = false ;
hasReplace | = pass - > pipeline - > info . stencil . failOp = = GPU_STENCIL_REPLACE ;
hasReplace | = pass - > pipeline - > info . stencil . depthFailOp = = GPU_STENCIL_REPLACE ;
hasReplace | = pass - > pipeline - > info . stencil . passOp = = GPU_STENCIL_REPLACE ;
if ( hasReplace & & test ! = COMPARE_NONE ) {
lovrCheck ( value = = pass - > pipeline - > info . stencil . value , " When stencil write is 'replace' and stencil test is active, their values must match " ) ;
}
switch ( test ) { // (Reversed compare mode)
case COMPARE_NONE : default : pass - > pipeline - > info . stencil . test = GPU_COMPARE_NONE ; break ;
case COMPARE_EQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_EQUAL ; break ;
case COMPARE_NEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_NEQUAL ; break ;
case COMPARE_LESS : pass - > pipeline - > info . stencil . test = GPU_COMPARE_GREATER ; break ;
case COMPARE_LEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_GEQUAL ; break ;
case COMPARE_GREATER : pass - > pipeline - > info . stencil . test = GPU_COMPARE_LESS ; break ;
case COMPARE_GEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_LEQUAL ; break ;
}
pass - > pipeline - > info . stencil . testMask = mask ;
if ( test ! = COMPARE_NONE ) pass - > pipeline - > info . stencil . value = value ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetStencilWrite ( Pass * pass , StencilAction actions [ 3 ] , uint8_t value , uint8_t mask ) {
bool hasReplace = actions [ 0 ] = = STENCIL_REPLACE | | actions [ 1 ] = = STENCIL_REPLACE | | actions [ 2 ] = = STENCIL_REPLACE ;
if ( hasReplace & & pass - > pipeline - > info . stencil . test ! = GPU_COMPARE_NONE ) {
lovrCheck ( value = = pass - > pipeline - > info . stencil . value , " When stencil write is 'replace' and stencil test is active, their values must match " ) ;
}
pass - > pipeline - > info . stencil . failOp = ( gpu_stencil_op ) actions [ 0 ] ;
pass - > pipeline - > info . stencil . depthFailOp = ( gpu_stencil_op ) actions [ 1 ] ;
pass - > pipeline - > info . stencil . passOp = ( gpu_stencil_op ) actions [ 2 ] ;
pass - > pipeline - > info . stencil . writeMask = mask ;
if ( hasReplace ) pass - > pipeline - > info . stencil . value = value ;
pass - > pipeline - > dirty = true ;
}
2022-06-06 19:38:15 +00:00
void lovrPassSetViewport ( Pass * pass , float viewport [ 4 ] , float depthRange [ 2 ] ) {
2022-07-30 22:20:01 +00:00
if ( pass - > info . type = = PASS_RENDER ) gpu_set_viewport ( pass - > stream , viewport , depthRange ) ;
2022-06-06 19:38:15 +00:00
memcpy ( pass - > pipeline - > viewport , viewport , 4 * sizeof ( float ) ) ;
memcpy ( pass - > pipeline - > depthRange , depthRange , 2 * sizeof ( float ) ) ;
}
2022-05-11 19:50:26 +00:00
void lovrPassSetWinding ( Pass * pass , Winding winding ) {
2022-08-03 05:00:11 +00:00
if ( pass - > cameraCount > 0 & & pass - > cameras [ 0 ] . projection [ 5 ] > 0.f ) { // Handedness change needs winding flip
winding = ! winding ;
}
2022-05-11 19:50:26 +00:00
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . winding ! = ( gpu_winding ) winding ;
pass - > pipeline - > info . rasterizer . winding = ( gpu_winding ) winding ;
}
void lovrPassSetWireframe ( Pass * pass , bool wireframe ) {
if ( state . features . wireframe ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . wireframe ! = ( gpu_winding ) wireframe ;
pass - > pipeline - > info . rasterizer . wireframe = wireframe ;
}
}
2022-05-24 06:10:11 +00:00
void lovrPassSendBuffer ( Pass * pass , const char * name , size_t length , uint32_t slot , Buffer * buffer , uint32_t offset , uint32_t extent ) {
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader , " A Shader must be active to send resources " ) ;
2022-06-04 21:28:23 +00:00
ShaderResource * resource = findShaderResource ( shader , name , length , slot ) ;
slot = resource - > binding ;
2022-05-24 06:10:11 +00:00
2022-07-04 00:26:31 +00:00
lovrCheck ( shader - > bufferMask & ( 1u < < slot ) , " Trying to send a Buffer to slot %d, but the active Shader doesn't have a Buffer in that slot " ) ;
2022-05-24 06:10:11 +00:00
lovrCheck ( offset < buffer - > size , " Buffer offset is past the end of the Buffer " ) ;
uint32_t limit ;
2022-07-04 00:26:31 +00:00
if ( shader - > storageMask & ( 1u < < slot ) ) {
2022-05-26 02:23:01 +00:00
lovrCheck ( ! lovrBufferIsTemporary ( buffer ) , " Temporary buffers can not be sent to storage buffer variables " , slot + 1 ) ;
2022-05-24 06:10:11 +00:00
lovrCheck ( ( offset & ( state . limits . storageBufferAlign - 1 ) ) = = 0 , " Storage buffer offset (%d) is not aligned to storageBufferAlign limit (%d) " , offset , state . limits . storageBufferAlign ) ;
limit = state . limits . storageBufferRange ;
} else {
lovrCheck ( ( offset & ( state . limits . uniformBufferAlign - 1 ) ) = = 0 , " Uniform buffer offset (%d) is not aligned to uniformBufferAlign limit (%d) " , offset , state . limits . uniformBufferAlign ) ;
limit = state . limits . uniformBufferRange ;
}
if ( extent = = 0 ) {
extent = MIN ( buffer - > size - offset , limit ) ;
} else {
lovrCheck ( offset + extent < = buffer - > size , " Buffer range goes past the end of the Buffer " ) ;
lovrCheck ( extent < = limit , " Buffer range exceeds storageBufferRange/uniformBufferRange limit " ) ;
}
pass - > bindings [ slot ] . buffer . object = buffer - > gpu ;
pass - > bindings [ slot ] . buffer . offset = offset ;
pass - > bindings [ slot ] . buffer . extent = extent ;
2022-07-04 00:26:31 +00:00
pass - > bindingMask | = ( 1u < < slot ) ;
2022-05-24 06:10:11 +00:00
pass - > bindingsDirty = true ;
2022-06-04 21:28:23 +00:00
gpu_phase phase = 0 ;
gpu_cache cache = 0 ;
if ( pass - > info . type = = PASS_RENDER ) {
if ( resource - > stageMask & GPU_STAGE_VERTEX ) phase | = GPU_PHASE_SHADER_VERTEX ;
if ( resource - > stageMask & GPU_STAGE_FRAGMENT ) phase | = GPU_PHASE_SHADER_FRAGMENT ;
2022-07-04 00:26:31 +00:00
cache = ( shader - > storageMask & ( 1u < < slot ) ) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_UNIFORM ;
2022-06-04 21:28:23 +00:00
} else {
phase = GPU_PHASE_SHADER_COMPUTE ;
2022-07-04 00:26:31 +00:00
cache = ( shader - > storageMask & ( 1u < < slot ) ) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_UNIFORM ; // TODO readonly
2022-06-04 21:28:23 +00:00
}
trackBuffer ( pass , buffer , phase , cache ) ;
2022-05-24 06:10:11 +00:00
}
void lovrPassSendTexture ( Pass * pass , const char * name , size_t length , uint32_t slot , Texture * texture ) {
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader , " A Shader must be active to send resources " ) ;
2022-06-04 21:28:23 +00:00
ShaderResource * resource = findShaderResource ( shader , name , length , slot ) ;
slot = resource - > binding ;
2022-05-24 06:10:11 +00:00
2022-07-04 00:26:31 +00:00
lovrCheck ( shader - > textureMask & ( 1u < < slot ) , " Trying to send a Texture to slot %d, but the active Shader doesn't have a Texture in that slot " ) ;
2022-05-24 06:10:11 +00:00
2022-07-04 00:26:31 +00:00
if ( shader - > storageMask & ( 1u < < slot ) ) {
2022-05-26 07:07:10 +00:00
lovrCheck ( texture - > info . usage & TEXTURE_STORAGE , " Textures must be created with the 'storage' usage to send them to image variables in shaders " ) ;
2022-05-24 06:10:11 +00:00
} else {
2022-05-26 07:07:10 +00:00
lovrCheck ( texture - > info . usage & TEXTURE_SAMPLE , " Textures must be created with the 'sample' usage to send them to sampler variables in shaders " ) ;
2022-05-24 06:10:11 +00:00
}
pass - > bindings [ slot ] . texture = texture - > gpu ;
2022-07-04 00:26:31 +00:00
pass - > bindingMask | = ( 1u < < slot ) ;
2022-05-24 06:10:11 +00:00
pass - > bindingsDirty = true ;
2022-06-04 21:28:23 +00:00
gpu_phase phase = 0 ;
gpu_cache cache = 0 ;
if ( pass - > info . type = = PASS_RENDER ) {
if ( resource - > stageMask & GPU_STAGE_VERTEX ) phase | = GPU_PHASE_SHADER_VERTEX ;
if ( resource - > stageMask & GPU_STAGE_FRAGMENT ) phase | = GPU_PHASE_SHADER_FRAGMENT ;
2022-07-04 00:26:31 +00:00
cache = ( shader - > storageMask & ( 1u < < slot ) ) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_TEXTURE ;
2022-06-04 21:28:23 +00:00
} else {
phase = GPU_PHASE_SHADER_COMPUTE ;
2022-07-04 00:26:31 +00:00
cache = ( shader - > storageMask & ( 1u < < slot ) ) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_TEXTURE ; // TODO readonly
2022-06-04 21:28:23 +00:00
}
trackTexture ( pass , texture , phase , cache ) ;
2022-05-24 06:10:11 +00:00
}
void lovrPassSendSampler ( Pass * pass , const char * name , size_t length , uint32_t slot , Sampler * sampler ) {
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader , " A Shader must be active to send resources " ) ;
2022-06-04 21:28:23 +00:00
ShaderResource * resource = findShaderResource ( shader , name , length , slot ) ;
slot = resource - > binding ;
2022-05-24 06:10:11 +00:00
2022-07-04 00:26:31 +00:00
lovrCheck ( shader - > samplerMask & ( 1u < < slot ) , " Trying to send a Sampler to slot %d, but the active Shader doesn't have a Sampler in that slot " ) ;
2022-05-24 06:10:11 +00:00
pass - > bindings [ slot ] . sampler = sampler - > gpu ;
2022-07-04 00:26:31 +00:00
pass - > bindingMask | = ( 1u < < slot ) ;
2022-05-24 06:10:11 +00:00
pass - > bindingsDirty = true ;
}
2022-06-06 01:54:26 +00:00
void lovrPassSendValue ( Pass * pass , const char * name , size_t length , void * * data , FieldType * type ) {
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader , " A Shader must be active to send resources " ) ;
uint32_t hash = ( uint32_t ) hash64 ( name , length ) ;
for ( uint32_t i = 0 ; i < shader - > constantCount ; i + + ) {
if ( shader - > constants [ i ] . hash = = hash ) {
* data = ( char * ) pass - > constants + shader - > constants [ i ] . offset ;
* type = shader - > constants [ i ] . type ;
pass - > constantsDirty = true ;
2022-07-02 01:34:31 +00:00
return ;
2022-06-06 01:54:26 +00:00
}
}
lovrThrow ( " Shader has no push constant named '%s' " , name ) ;
}
2022-08-09 03:36:22 +00:00
static void bindPipeline ( Pass * pass , Draw * draw , Shader * shader ) {
2022-05-30 22:36:31 +00:00
Pipeline * pipeline = pass - > pipeline ;
2022-06-10 06:05:32 +00:00
if ( pipeline - > info . drawMode ! = ( gpu_draw_mode ) draw - > mode ) {
pipeline - > info . drawMode = ( gpu_draw_mode ) draw - > mode ;
2022-05-30 22:36:31 +00:00
pipeline - > dirty = true ;
}
2022-06-24 00:07:39 +00:00
if ( ! pipeline - > shader & & pipeline - > info . shader ! = shader - > gpu ) {
2022-05-30 22:36:31 +00:00
pipeline - > info . shader = shader - > gpu ;
pipeline - > info . flags = NULL ;
pipeline - > info . flagCount = 0 ;
pipeline - > dirty = true ;
}
2022-08-09 03:36:22 +00:00
// Vertex formats
if ( pipeline - > formatHash ! = 1 + draw - > vertex . format ) {
if ( ! draw - > vertex . buffer ) {
pipeline - > formatHash = 1 + draw - > vertex . format ;
pipeline - > info . vertex = state . vertexFormats [ draw - > vertex . format ] ;
pipeline - > dirty = true ;
if ( shader - > hasCustomAttributes ) {
for ( uint32_t i = 0 ; i < shader - > attributeCount ; i + + ) {
if ( shader - > attributes [ i ] . location < 10 ) {
pipeline - > info . vertex . attributes [ pipeline - > info . vertex . attributeCount + + ] = ( gpu_attribute ) {
. buffer = 1 ,
. location = shader - > attributes [ i ] . location ,
. type = GPU_TYPE_F32x4 ,
. offset = shader - > attributes [ i ] . location = = LOCATION_COLOR ? 16 : 0
} ;
}
2022-05-30 22:36:31 +00:00
}
}
2022-08-09 03:36:22 +00:00
} else {
pipeline - > formatHash = draw - > vertex . buffer - > hash ;
pipeline - > info . vertex . bufferCount = 2 ;
pipeline - > info . vertex . attributeCount = shader - > attributeCount ;
pipeline - > info . vertex . bufferStrides [ 0 ] = draw - > vertex . buffer - > info . stride ;
pipeline - > info . vertex . bufferStrides [ 1 ] = 0 ;
pipeline - > dirty = true ;
2022-06-16 03:46:43 +00:00
2022-08-09 03:36:22 +00:00
for ( uint32_t i = 0 ; i < shader - > attributeCount ; i + + ) {
ShaderAttribute * attribute = & shader - > attributes [ i ] ;
bool found = false ;
for ( uint32_t j = 0 ; j < draw - > vertex . buffer - > info . fieldCount ; j + + ) {
BufferField field = draw - > vertex . buffer - > info . fields [ j ] ;
lovrCheck ( field . type < FIELD_MAT2 , " Currently, matrix and index types can not be used in vertex buffers " ) ;
if ( field . hash ? ( field . hash = = attribute - > hash ) : ( field . location = = attribute - > location ) ) {
pipeline - > info . vertex . attributes [ i ] = ( gpu_attribute ) {
. buffer = 0 ,
. location = attribute - > location ,
. offset = field . offset ,
. type = field . type
} ;
found = true ;
break ;
}
}
2022-06-16 03:46:43 +00:00
2022-08-09 03:36:22 +00:00
if ( ! found ) {
2022-06-16 03:46:43 +00:00
pipeline - > info . vertex . attributes [ i ] = ( gpu_attribute ) {
2022-08-09 03:36:22 +00:00
. buffer = 1 ,
2022-06-16 03:46:43 +00:00
. location = attribute - > location ,
2022-08-09 03:36:22 +00:00
. offset = attribute - > location = = LOCATION_COLOR ? 16 : 0 ,
. type = GPU_TYPE_F32x4
2022-06-16 03:46:43 +00:00
} ;
}
}
}
2022-05-30 22:36:31 +00:00
}
if ( ! pipeline - > dirty ) {
return ;
}
uint64_t hash = hash64 ( & pipeline - > info , sizeof ( pipeline - > info ) ) ;
uint64_t index = map_get ( & state . pipelineLookup , hash ) ;
if ( index = = MAP_NIL ) {
gpu_pipeline * gpu = malloc ( gpu_sizeof_pipeline ( ) ) ;
lovrAssert ( gpu , " Out of memory " ) ;
gpu_pipeline_init_graphics ( gpu , & pipeline - > info ) ;
index = state . pipelines . length ;
arr_push ( & state . pipelines , gpu ) ;
map_set ( & state . pipelineLookup , hash , index ) ;
}
gpu_bind_pipeline ( pass - > stream , state . pipelines . data [ index ] , false ) ;
pipeline - > dirty = false ;
}
2022-08-09 03:36:22 +00:00
static void bindBundles ( Pass * pass , Draw * draw , Shader * shader ) {
size_t stack = tempPush ( ) ;
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
gpu_bundle * bundles [ 3 ] ;
uint32_t bundleMask = 0 ;
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
// Set 0 - Builtins
if ( pass - > info . type = = PASS_RENDER ) {
bool builtinsDirty = false ;
if ( pass - > cameraDirty ) {
for ( uint32_t i = 0 ; i < pass - > cameraCount ; i + + ) {
mat4_init ( pass - > cameras [ i ] . viewProjection , pass - > cameras [ i ] . projection ) ;
mat4_init ( pass - > cameras [ i ] . inverseProjection , pass - > cameras [ i ] . projection ) ;
mat4_mul ( pass - > cameras [ i ] . viewProjection , pass - > cameras [ i ] . view ) ;
mat4_invert ( pass - > cameras [ i ] . inverseProjection ) ;
}
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
uint32_t size = pass - > cameraCount * sizeof ( Camera ) ;
void * data = gpu_map ( pass - > builtins [ 1 ] . buffer . object , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
memcpy ( data , pass - > cameras , size ) ;
pass - > cameraDirty = false ;
builtinsDirty = true ;
}
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
if ( pass - > drawCount % 256 = = 0 ) {
uint32_t size = 256 * sizeof ( DrawData ) ;
pass - > drawData = gpu_map ( pass - > builtins [ 2 ] . buffer . object , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
builtinsDirty = true ;
}
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
if ( pass - > samplerDirty ) {
Sampler * sampler = pass - > pipeline - > sampler ? pass - > pipeline - > sampler : state . defaultSamplers [ FILTER_LINEAR ] ;
pass - > builtins [ 3 ] . sampler = sampler - > gpu ;
pass - > samplerDirty = false ;
builtinsDirty = true ;
}
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
if ( builtinsDirty ) {
gpu_bundle_info bundleInfo = {
. layout = state . layouts . data [ state . builtinLayout ] . gpu ,
. bindings = pass - > builtins ,
. count = COUNTOF ( pass - > builtins )
} ;
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
bundles [ 0 ] = getBundle ( state . builtinLayout ) ;
gpu_bundle_write ( & bundles [ 0 ] , & bundleInfo , 1 ) ;
bundleMask | = ( 1 < < 0 ) ;
2022-05-30 22:36:31 +00:00
}
2022-08-09 03:36:22 +00:00
// Draw data
float m [ 16 ] ;
float * transform ;
if ( draw - > transform ) {
transform = mat4_mul ( mat4_init ( m , pass - > transform ) , draw - > transform ) ;
} else {
transform = pass - > transform ;
}
float cofactor [ 16 ] ;
mat4_init ( cofactor , transform ) ;
cofactor [ 12 ] = 0.f ;
cofactor [ 13 ] = 0.f ;
cofactor [ 14 ] = 0.f ;
cofactor [ 15 ] = 1.f ;
mat4_cofactor ( cofactor ) ;
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
memcpy ( pass - > drawData - > transform , transform , 64 ) ;
memcpy ( pass - > drawData - > cofactor , cofactor , 64 ) ;
memcpy ( pass - > drawData - > color , pass - > pipeline - > color , 16 ) ;
pass - > drawData + + ;
2022-05-30 22:36:31 +00:00
}
2022-08-09 03:36:22 +00:00
// Set 1 - Material
if ( pass - > info . type = = PASS_RENDER ) {
if ( draw - > material & & draw - > material ! = pass - > pipeline - > material ) {
trackMaterial ( pass , draw - > material , GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT , GPU_CACHE_TEXTURE ) ;
pass - > materialDirty = true ;
bundles [ 1 ] = draw - > material - > bundle ;
bundleMask | = ( 1 < < 1 ) ;
} else if ( pass - > materialDirty ) {
Material * material = pass - > pipeline - > material ? pass - > pipeline - > material : state . defaultMaterial ;
trackMaterial ( pass , material , GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT , GPU_CACHE_TEXTURE ) ;
pass - > materialDirty = false ;
bundles [ 1 ] = material - > bundle ;
bundleMask | = ( 1 < < 1 ) ;
} else {
bundles [ 1 ] = ( pass - > pipeline - > material ? pass - > pipeline - > material : state . defaultMaterial ) - > bundle ;
}
2022-06-08 03:42:10 +00:00
}
2022-08-09 03:36:22 +00:00
// Set 2 - Resources
if ( pass - > bindingsDirty & & shader - > resourceCount > 0 ) {
gpu_binding * bindings = tempAlloc ( shader - > resourceCount * sizeof ( gpu_binding ) ) ;
for ( uint32_t i = 0 ; i < shader - > resourceCount ; i + + ) {
bindings [ i ] = pass - > bindings [ shader - > resources [ i ] . binding ] ;
}
gpu_bundle_info info = {
. layout = state . layouts . data [ shader - > layout ] . gpu ,
. bindings = bindings ,
. count = shader - > resourceCount
2022-05-30 22:36:31 +00:00
} ;
2022-08-09 03:36:22 +00:00
gpu_bundle * bundle = getBundle ( shader - > layout ) ;
gpu_bundle_write ( & bundle , & info , 1 ) ;
pass - > bindingsDirty = false ;
2022-05-30 22:36:31 +00:00
2022-08-09 03:36:22 +00:00
uint32_t set = pass - > info . type = = PASS_RENDER ? 2 : 0 ;
bundleMask | = ( 1 < < set ) ;
bundles [ set ] = bundle ;
2022-05-30 22:36:31 +00:00
}
2022-08-09 03:36:22 +00:00
// Bind
if ( bundleMask ) {
uint32_t first = 0 ;
while ( ~ bundleMask & 0x1 ) {
bundleMask > > = 1 ;
first + + ;
}
uint32_t count = 0 ;
while ( bundleMask ) {
bundleMask > > = 1 ;
count + + ;
}
gpu_bind_bundles ( pass - > stream , shader - > gpu , bundles + first , first , count , NULL , 0 ) ;
2022-06-17 06:49:09 +00:00
}
2022-08-09 03:36:22 +00:00
tempPop ( stack ) ;
2022-06-17 06:49:09 +00:00
}
2022-08-09 03:36:22 +00:00
static void bindBuffers ( Pass * pass , Draw * draw ) {
2022-07-12 03:52:35 +00:00
Shape * cache = NULL ;
if ( draw - > hash ) {
cache = & pass - > shapeCache [ draw - > hash & ( COUNTOF ( pass - > shapeCache ) - 1 ) ] ;
if ( cache - > hash = = draw - > hash ) {
2022-07-13 00:19:30 +00:00
if ( pass - > vertexBuffer ! = cache - > vertices ) {
gpu_bind_vertex_buffers ( pass - > stream , & cache - > vertices , NULL , 0 , 1 ) ;
pass - > vertexBuffer = cache - > vertices ;
}
if ( pass - > indexBuffer ! = cache - > indices ) {
gpu_bind_index_buffer ( pass - > stream , cache - > indices , 0 , GPU_INDEX_U16 ) ;
pass - > indexBuffer = cache - > indices ;
}
2022-07-12 03:52:35 +00:00
* draw - > vertex . pointer = NULL ;
* draw - > index . pointer = NULL ;
return ;
}
}
2022-05-30 22:36:31 +00:00
if ( ! draw - > vertex . buffer & & draw - > vertex . count > 0 ) {
2022-06-04 18:19:28 +00:00
lovrCheck ( draw - > vertex . count < UINT16_MAX , " This draw has too many vertices (max is 65534), try splitting it up into multiple draws or using a Buffer " ) ;
2022-06-19 06:31:51 +00:00
uint32_t stride = state . vertexFormats [ draw - > vertex . format ] . bufferStrides [ 0 ] ;
2022-05-30 22:36:31 +00:00
uint32_t size = draw - > vertex . count * stride ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
2022-07-12 03:52:35 +00:00
* draw - > vertex . pointer = gpu_map ( scratchpad , size , stride , GPU_MAP_WRITE ) ;
2022-05-30 22:36:31 +00:00
2022-06-19 06:31:51 +00:00
gpu_bind_vertex_buffers ( pass - > stream , & scratchpad , NULL , 0 , 1 ) ;
2022-07-12 03:52:35 +00:00
pass - > vertexBuffer = scratchpad ;
2022-05-30 22:36:31 +00:00
} else if ( draw - > vertex . buffer & & draw - > vertex . buffer - > gpu ! = pass - > vertexBuffer ) {
lovrCheck ( draw - > vertex . buffer - > info . stride < = state . limits . vertexBufferStride , " Vertex buffer stride exceeds vertexBufferStride limit " ) ;
2022-06-19 06:31:51 +00:00
gpu_bind_vertex_buffers ( pass - > stream , & draw - > vertex . buffer - > gpu , NULL , 0 , 1 ) ;
2022-05-30 22:36:31 +00:00
pass - > vertexBuffer = draw - > vertex . buffer - > gpu ;
2022-06-04 21:28:23 +00:00
trackBuffer ( pass , draw - > vertex . buffer , GPU_PHASE_INPUT_VERTEX , GPU_CACHE_VERTEX ) ;
2022-05-30 22:36:31 +00:00
}
if ( ! draw - > index . buffer & & draw - > index . count > 0 ) {
2022-07-12 03:52:35 +00:00
uint32_t size = draw - > index . count * sizeof ( uint16_t ) ;
2022-05-30 22:36:31 +00:00
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
2022-07-12 03:52:35 +00:00
* draw - > index . pointer = gpu_map ( scratchpad , size , sizeof ( uint16_t ) , GPU_MAP_WRITE ) ;
2022-05-30 22:36:31 +00:00
2022-07-12 03:52:35 +00:00
gpu_bind_index_buffer ( pass - > stream , scratchpad , 0 , GPU_INDEX_U16 ) ;
pass - > indexBuffer = scratchpad ;
2022-05-30 22:36:31 +00:00
} else if ( draw - > index . buffer & & draw - > index . buffer - > gpu ! = pass - > indexBuffer ) {
gpu_index_type type = draw - > index . buffer - > info . stride = = 4 ? GPU_INDEX_U32 : GPU_INDEX_U16 ;
gpu_bind_index_buffer ( pass - > stream , draw - > index . buffer - > gpu , 0 , type ) ;
pass - > indexBuffer = draw - > index . buffer - > gpu ;
2022-06-04 21:28:23 +00:00
trackBuffer ( pass , draw - > index . buffer , GPU_PHASE_INPUT_INDEX , GPU_CACHE_INDEX ) ;
2022-05-30 22:36:31 +00:00
}
2022-07-12 03:52:35 +00:00
if ( cache ) {
cache - > hash = draw - > hash ;
cache - > vertices = pass - > vertexBuffer ;
cache - > indices = pass - > indexBuffer ;
}
2022-05-30 22:36:31 +00:00
}
2022-08-09 03:36:22 +00:00
static void pushConstants ( Pass * pass , Shader * shader ) {
if ( pass - > constantsDirty & & shader - > constantSize > 0 ) {
gpu_push_constants ( pass - > stream , shader - > gpu , pass - > constants , shader - > constantSize ) ;
pass - > constantsDirty = false ;
}
}
2022-05-30 22:36:31 +00:00
static void lovrPassDraw ( Pass * pass , Draw * draw ) {
lovrCheck ( pass - > info . type = = PASS_RENDER , " This function can only be called on a render pass " ) ;
Shader * shader = pass - > pipeline - > shader ? pass - > pipeline - > shader : lovrGraphicsGetDefaultShader ( draw - > shader ) ;
2022-08-09 03:36:22 +00:00
bindPipeline ( pass , draw , shader ) ;
bindBundles ( pass , draw , shader ) ;
bindBuffers ( pass , draw ) ;
pushConstants ( pass , shader ) ;
2022-05-30 22:36:31 +00:00
uint32_t defaultCount = draw - > index . count > 0 ? draw - > index . count : draw - > vertex . count ;
uint32_t count = draw - > count > 0 ? draw - > count : defaultCount ;
uint32_t instances = MAX ( draw - > instances , 1 ) ;
uint32_t id = pass - > drawCount & 0xff ;
2022-06-10 06:38:33 +00:00
if ( draw - > index . buffer | | draw - > index . count > 0 ) {
gpu_draw_indexed ( pass - > stream , count , instances , draw - > start , draw - > base , id ) ;
2022-05-30 22:36:31 +00:00
} else {
2022-06-10 06:38:33 +00:00
gpu_draw ( pass - > stream , count , instances , draw - > start , id ) ;
2022-05-30 22:36:31 +00:00
}
pass - > drawCount + + ;
}
2022-06-04 08:33:50 +00:00
void lovrPassPoints ( Pass * pass , uint32_t count , float * * points ) {
2022-05-30 22:36:31 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_POINTS ,
2022-05-30 22:36:31 +00:00
. vertex . format = VERTEX_POINT ,
2022-06-04 08:33:50 +00:00
. vertex . pointer = ( void * * ) points ,
2022-05-30 22:36:31 +00:00
. vertex . count = count
} ) ;
}
2022-06-04 08:33:50 +00:00
void lovrPassLine ( Pass * pass , uint32_t count , float * * points ) {
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_LINES ,
2022-06-04 08:33:50 +00:00
. vertex . format = VERTEX_POINT ,
. vertex . pointer = ( void * * ) points ,
. vertex . count = count ,
. index . pointer = ( void * * ) & indices ,
. index . count = 2 * ( count - 1 )
} ) ;
for ( uint32_t i = 0 ; i < count - 1 ; i + + ) {
indices [ 2 * i + 0 ] = i ;
indices [ 2 * i + 1 ] = i + 1 ;
}
}
2022-06-22 07:05:26 +00:00
void lovrPassPlane ( Pass * pass , float * transform , DrawStyle style , uint32_t cols , uint32_t rows ) {
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_PLANE , style , cols , rows } ;
2022-06-04 08:34:13 +00:00
ShapeVertex * vertices ;
uint16_t * indices ;
2022-06-04 18:19:28 +00:00
uint32_t vertexCount = ( cols + 1 ) * ( rows + 1 ) ;
2022-06-22 07:05:26 +00:00
uint32_t indexCount ;
if ( style = = STYLE_LINE ) {
indexCount = 2 * ( rows + 1 ) + 2 * ( cols + 1 ) ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_LINES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
2022-07-15 02:23:02 +00:00
. index . count = indexCount
2022-06-22 07:05:26 +00:00
} ) ;
} else {
indexCount = ( cols * rows ) * 6 ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
}
2022-06-04 08:34:13 +00:00
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-04 18:19:28 +00:00
for ( uint32_t y = 0 ; y < = rows ; y + + ) {
float v = y * ( 1.f / rows ) ;
for ( uint32_t x = 0 ; x < = cols ; x + + ) {
float u = x * ( 1.f / cols ) ;
* vertices + + = ( ShapeVertex ) {
. position = { u - .5f , .5f - v , 0.f } ,
. normal = { 0.f , 0.f , 1.f } ,
. uv = { u , v }
} ;
2022-06-04 08:34:13 +00:00
}
}
2022-06-22 07:05:26 +00:00
if ( style = = STYLE_LINE ) {
for ( uint32_t y = 0 ; y < = rows ; y + + ) {
uint16_t a = y * ( cols + 1 ) ;
uint16_t b = a + cols ;
uint16_t line [ ] = { a , b } ;
memcpy ( indices , line , sizeof ( line ) ) ;
indices + = COUNTOF ( line ) ;
}
for ( uint32_t x = 0 ; x < = cols ; x + + ) {
uint16_t a = x ;
uint16_t b = x + ( ( cols + 1 ) * rows ) ;
uint16_t line [ ] = { a , b } ;
memcpy ( indices , line , sizeof ( line ) ) ;
indices + = COUNTOF ( line ) ;
}
} else {
for ( uint32_t y = 0 ; y < rows ; y + + ) {
for ( uint32_t x = 0 ; x < cols ; x + + ) {
uint16_t a = ( y * ( cols + 1 ) ) + x ;
uint16_t b = a + 1 ;
uint16_t c = a + cols + 1 ;
uint16_t d = a + cols + 2 ;
2022-07-04 03:07:00 +00:00
uint16_t cell [ ] = { a , c , b , b , c , d } ;
2022-06-22 07:05:26 +00:00
memcpy ( indices , cell , sizeof ( cell ) ) ;
indices + = COUNTOF ( cell ) ;
}
2022-06-04 08:34:13 +00:00
}
}
}
2022-06-22 07:05:26 +00:00
void lovrPassBox ( Pass * pass , float * transform , DrawStyle style ) {
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_BOX , style } ;
ShapeVertex * vertices ;
uint16_t * indices ;
2022-06-22 07:05:26 +00:00
if ( style = = STYLE_LINE ) {
2022-07-12 03:52:35 +00:00
static ShapeVertex vertexData [ ] = {
2022-06-22 07:05:26 +00:00
{ { - .5f , .5f , - .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } , // Front
{ { .5f , .5f , - .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { .5f , - .5f , - .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { - .5f , - .5f , - .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { - .5f , .5f , .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } , // Back
{ { .5f , .5f , .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { .5f , - .5f , .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { - .5f , - .5f , .5f } , { 0.f , 0.f , 0.f } , { 0.f , 0.f } }
} ;
2022-06-04 18:28:35 +00:00
2022-07-12 03:52:35 +00:00
static uint16_t indexData [ ] = {
2022-06-22 07:05:26 +00:00
0 , 1 , 1 , 2 , 2 , 3 , 3 , 0 , // Front
4 , 5 , 5 , 6 , 6 , 7 , 7 , 4 , // Back
0 , 4 , 1 , 5 , 2 , 6 , 3 , 7 // Connections
} ;
2022-06-04 18:28:35 +00:00
2022-06-22 07:05:26 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_LINES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
2022-07-12 03:52:35 +00:00
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = COUNTOF ( vertexData ) ,
. index . pointer = ( void * * ) & indices ,
. index . count = COUNTOF ( indexData )
2022-06-22 07:05:26 +00:00
} ) ;
2022-07-12 03:52:35 +00:00
if ( vertices ) {
memcpy ( vertices , vertexData , sizeof ( vertexData ) ) ;
memcpy ( indices , indexData , sizeof ( indexData ) ) ;
}
2022-06-22 07:05:26 +00:00
} else {
2022-07-12 03:52:35 +00:00
static ShapeVertex vertexData [ ] = {
2022-06-22 07:05:26 +00:00
{ { - .5f , - .5f , - .5f } , { 0.f , 0.f , - 1.f } , { 0.f , 0.f } } , // Front
{ { - .5f , .5f , - .5f } , { 0.f , 0.f , - 1.f } , { 0.f , 1.f } } ,
{ { .5f , - .5f , - .5f } , { 0.f , 0.f , - 1.f } , { 1.f , 0.f } } ,
{ { .5f , .5f , - .5f } , { 0.f , 0.f , - 1.f } , { 1.f , 1.f } } ,
{ { .5f , .5f , - .5f } , { 1.f , 0.f , 0.f } , { 0.f , 1.f } } , // Right
{ { .5f , .5f , .5f } , { 1.f , 0.f , 0.f } , { 1.f , 1.f } } ,
{ { .5f , - .5f , - .5f } , { 1.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { .5f , - .5f , .5f } , { 1.f , 0.f , 0.f } , { 1.f , 0.f } } ,
{ { .5f , - .5f , .5f } , { 0.f , 0.f , 1.f } , { 0.f , 0.f } } , // Back
{ { .5f , .5f , .5f } , { 0.f , 0.f , 1.f } , { 0.f , 1.f } } ,
{ { - .5f , - .5f , .5f } , { 0.f , 0.f , 1.f } , { 1.f , 0.f } } ,
{ { - .5f , .5f , .5f } , { 0.f , 0.f , 1.f } , { 1.f , 1.f } } ,
{ { - .5f , .5f , .5f } , { - 1.f , 0.f , 0.f } , { 0.f , 1.f } } , // Left
{ { - .5f , .5f , - .5f } , { - 1.f , 0.f , 0.f } , { 1.f , 1.f } } ,
{ { - .5f , - .5f , .5f } , { - 1.f , 0.f , 0.f } , { 0.f , 0.f } } ,
{ { - .5f , - .5f , - .5f } , { - 1.f , 0.f , 0.f } , { 1.f , 0.f } } ,
{ { - .5f , - .5f , - .5f } , { 0.f , - 1.f , 0.f } , { 0.f , 0.f } } , // Bottom
{ { .5f , - .5f , - .5f } , { 0.f , - 1.f , 0.f } , { 1.f , 0.f } } ,
{ { - .5f , - .5f , .5f } , { 0.f , - 1.f , 0.f } , { 0.f , 1.f } } ,
{ { .5f , - .5f , .5f } , { 0.f , - 1.f , 0.f } , { 1.f , 1.f } } ,
{ { - .5f , .5f , - .5f } , { 0.f , 1.f , 0.f } , { 0.f , 1.f } } , // Top
{ { - .5f , .5f , .5f } , { 0.f , 1.f , 0.f } , { 0.f , 0.f } } ,
{ { .5f , .5f , - .5f } , { 0.f , 1.f , 0.f } , { 1.f , 1.f } } ,
{ { .5f , .5f , .5f } , { 0.f , 1.f , 0.f } , { 1.f , 0.f } }
} ;
2022-06-04 18:28:35 +00:00
2022-07-12 03:52:35 +00:00
static uint16_t indexData [ ] = {
2022-06-22 07:05:26 +00:00
0 , 1 , 2 , 2 , 1 , 3 ,
4 , 5 , 6 , 6 , 5 , 7 ,
8 , 9 , 10 , 10 , 9 , 11 ,
12 , 13 , 14 , 14 , 13 , 15 ,
16 , 17 , 18 , 18 , 17 , 19 ,
20 , 21 , 22 , 22 , 21 , 23
} ;
2022-06-04 18:28:35 +00:00
2022-06-22 07:05:26 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
2022-07-12 03:52:35 +00:00
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = COUNTOF ( vertexData ) ,
. index . pointer = ( void * * ) & indices ,
. index . count = COUNTOF ( indexData )
2022-06-22 07:05:26 +00:00
} ) ;
2022-07-12 03:52:35 +00:00
if ( vertices ) {
memcpy ( vertices , vertexData , sizeof ( vertexData ) ) ;
memcpy ( indices , indexData , sizeof ( indexData ) ) ;
}
2022-06-22 07:05:26 +00:00
}
2022-06-04 18:28:35 +00:00
}
2022-06-22 07:05:26 +00:00
void lovrPassCircle ( Pass * pass , float * transform , DrawStyle style , float angle1 , float angle2 , uint32_t segments ) {
2022-06-05 20:12:49 +00:00
if ( fabsf ( angle1 - angle2 ) > = 2.f * ( float ) M_PI ) {
angle1 = 0.f ;
angle2 = 2.f * ( float ) M_PI ;
}
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_CIRCLE , style , FLOAT_BITS ( angle1 ) , FLOAT_BITS ( angle2 ) , segments } ;
2022-06-24 02:52:37 +00:00
ShapeVertex * vertices ;
uint16_t * indices ;
2022-06-22 07:05:26 +00:00
if ( style = = STYLE_LINE ) {
uint32_t vertexCount = segments + 1 ;
uint32_t indexCount = segments * 2 ;
2022-06-05 20:12:49 +00:00
2022-06-22 07:05:26 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_LINES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-22 07:05:26 +00:00
} else {
uint32_t vertexCount = segments + 2 ;
uint32_t indexCount = segments * 3 ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-22 07:05:26 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
2022-06-05 20:12:49 +00:00
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-22 07:05:26 +00:00
// Center
* vertices + + = ( ShapeVertex ) { { 0.f , 0.f , 0.f } , { 0.f , 0.f , 1.f } , { .5f , .5f } } ;
}
2022-06-05 20:12:49 +00:00
float angleShift = ( angle2 - angle1 ) / segments ;
for ( uint32_t i = 0 ; i < = segments ; i + + ) {
float theta = angle1 + i * angleShift ;
2022-06-24 04:23:16 +00:00
float x = cosf ( theta ) ;
float y = sinf ( theta ) ;
2022-06-05 20:12:49 +00:00
* vertices + + = ( ShapeVertex ) { { x , y , 0.f } , { 0.f , 0.f , 1.f } , { x + .5f , .5f - y } } ;
}
2022-06-22 07:05:26 +00:00
if ( style = = STYLE_LINE ) {
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t segment [ ] = { i , i + 1 } ;
memcpy ( indices , segment , sizeof ( segment ) ) ;
indices + = COUNTOF ( segment ) ;
}
} else {
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t wedge [ ] = { 0 , i + 1 , i + 2 } ;
memcpy ( indices , wedge , sizeof ( wedge ) ) ;
indices + = COUNTOF ( wedge ) ;
}
2022-06-05 20:12:49 +00:00
}
}
2022-06-24 04:23:16 +00:00
void lovrPassSphere ( Pass * pass , float * transform , uint32_t segmentsH , uint32_t segmentsV ) {
uint32_t vertexCount = 2 + ( segmentsH + 1 ) * ( segmentsV - 1 ) ;
uint32_t indexCount = 2 * 3 * segmentsH + segmentsH * ( segmentsV - 2 ) * 6 ;
ShapeVertex * vertices ;
uint16_t * indices ;
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_SPHERE , segmentsH , segmentsV } ;
2022-06-24 04:23:16 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-24 04:23:16 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
2022-07-12 03:52:35 +00:00
. index . count = indexCount ,
2022-06-24 04:23:16 +00:00
} ) ;
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-24 04:23:16 +00:00
// Top
* vertices + + = ( ShapeVertex ) { { 0.f , 1.f , 0.f } , { 0.f , 1.f , 0.f } , { .5f , 0.f } } ;
// Rings
for ( uint32_t i = 1 ; i < segmentsV ; i + + ) {
float v = i / ( float ) segmentsV ;
float phi = v * ( float ) M_PI ;
float sinphi = sinf ( phi ) ;
float cosphi = cosf ( phi ) ;
for ( uint32_t j = 0 ; j < = segmentsH ; j + + ) {
float u = j / ( float ) segmentsH ;
float theta = u * 2.f * ( float ) M_PI ;
float sintheta = sinf ( theta ) ;
float costheta = cosf ( theta ) ;
float x = sintheta * sinphi ;
float y = cosphi ;
float z = - costheta * sinphi ;
* vertices + + = ( ShapeVertex ) { { x , y , z } , { x , y , z } , { u , v } } ;
}
}
// Bottom
* vertices + + = ( ShapeVertex ) { { 0.f , - 1.f , 0.f } , { 0.f , - 1.f , 0.f } , { .5f , 1.f } } ;
// Top
for ( uint32_t i = 0 ; i < segmentsH ; i + + ) {
uint16_t wedge [ ] = { 0 , i + 2 , i + 1 } ;
memcpy ( indices , wedge , sizeof ( wedge ) ) ;
indices + = COUNTOF ( wedge ) ;
}
// Rings
for ( uint32_t i = 0 ; i < segmentsV - 2 ; i + + ) {
for ( uint32_t j = 0 ; j < segmentsH ; j + + ) {
uint16_t a = 1 + i * ( segmentsH + 1 ) + 0 + j ;
uint16_t b = 1 + i * ( segmentsH + 1 ) + 1 + j ;
uint16_t c = 1 + i * ( segmentsH + 1 ) + 0 + segmentsH + 1 + j ;
uint16_t d = 1 + i * ( segmentsH + 1 ) + 1 + segmentsH + 1 + j ;
uint16_t quad [ ] = { a , b , c , c , b , d } ;
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
}
// Bottom
for ( uint32_t i = 0 ; i < segmentsH ; i + + ) {
uint16_t wedge [ ] = { vertexCount - 1 , vertexCount - 1 - ( i + 2 ) , vertexCount - 1 - ( i + 1 ) } ;
memcpy ( indices , wedge , sizeof ( wedge ) ) ;
indices + = COUNTOF ( wedge ) ;
}
}
2022-06-25 06:01:22 +00:00
void lovrPassCylinder ( Pass * pass , float * transform , bool capped , float angle1 , float angle2 , uint32_t segments ) {
if ( fabsf ( angle1 - angle2 ) > = 2.f * ( float ) M_PI ) {
angle1 = 0.f ;
angle2 = 2.f * ( float ) M_PI ;
}
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_CYLINDER , capped , FLOAT_BITS ( angle1 ) , FLOAT_BITS ( angle2 ) , segments } ;
2022-06-25 06:01:22 +00:00
uint32_t vertexCount = 2 * ( segments + 1 ) ;
uint32_t indexCount = 6 * segments ;
ShapeVertex * vertices ;
uint16_t * indices ;
if ( capped ) {
vertexCount * = 2 ;
vertexCount + = 2 ;
indexCount + = 3 * segments * 2 ;
}
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-25 06:01:22 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-25 06:01:22 +00:00
float angleShift = ( angle2 - angle1 ) / segments ;
// Tube
for ( uint32_t i = 0 ; i < = segments ; i + + ) {
float theta = angle1 + i * angleShift ;
float x = cosf ( theta ) ;
float y = sinf ( theta ) ;
* vertices + + = ( ShapeVertex ) { { x , y , - .5f } , { x , y , 0.f } , { x + .5f , .5f - y } } ;
* vertices + + = ( ShapeVertex ) { { x , y , .5f } , { x , y , 0.f } , { x + .5f , .5f - y } } ;
}
// Tube quads
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t a = i * 2 + 0 ;
uint16_t b = i * 2 + 1 ;
uint16_t c = i * 2 + 2 ;
uint16_t d = i * 2 + 3 ;
2022-07-31 07:24:43 +00:00
uint16_t quad [ ] = { a , c , b , b , c , d } ;
2022-06-25 06:01:22 +00:00
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
if ( capped ) {
// Cap centers
2022-07-31 07:24:43 +00:00
* vertices + + = ( ShapeVertex ) { { 0.f , 0.f , - .5f } , { 0.f , 0.f , - 1.f } , { .5f , .5f } } ;
* vertices + + = ( ShapeVertex ) { { 0.f , 0.f , .5f } , { 0.f , 0.f , 1.f } , { .5f , .5f } } ;
2022-06-25 06:01:22 +00:00
// Caps
for ( uint32_t i = 0 ; i < = segments ; i + + ) {
float theta = angle1 + i * angleShift ;
float x = cosf ( theta ) ;
float y = sinf ( theta ) ;
2022-07-31 07:24:43 +00:00
* vertices + + = ( ShapeVertex ) { { x , y , - .5f } , { 0.f , 0.f , - 1.f } , { x + .5f , y - .5f } } ;
* vertices + + = ( ShapeVertex ) { { x , y , .5f } , { 0.f , 0.f , 1.f } , { x + .5f , y - .5f } } ;
2022-06-25 06:01:22 +00:00
}
// Cap wedges
uint16_t base = 2 * ( segments + 1 ) ;
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t a = base + 0 ;
uint16_t b = base + ( i + 1 ) * 2 ;
uint16_t c = base + ( i + 2 ) * 2 ;
2022-07-31 07:24:43 +00:00
uint16_t wedge1 [ ] = { a + 0 , c + 0 , b + 0 } ;
2022-06-25 06:01:22 +00:00
uint16_t wedge2 [ ] = { a + 1 , b + 1 , c + 1 } ;
memcpy ( indices + 0 , wedge1 , sizeof ( wedge1 ) ) ;
memcpy ( indices + 3 , wedge2 , sizeof ( wedge2 ) ) ;
indices + = 6 ;
}
}
}
2022-07-17 23:38:00 +00:00
void lovrPassCone ( Pass * pass , float * transform , uint32_t segments ) {
uint32_t key [ ] = { SHAPE_CONE , segments } ;
uint32_t vertexCount = 2 * segments + 1 ;
uint32_t indexCount = 3 * ( segments - 2 ) + 3 * segments ;
ShapeVertex * vertices ;
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
. hash = hash64 ( key , sizeof ( key ) ) ,
. mode = MESH_TRIANGLES ,
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
if ( ! vertices ) {
return ;
}
for ( uint32_t i = 0 ; i < segments ; i + + ) {
float theta = i * 2.f * ( float ) M_PI / segments ;
float x = cosf ( theta ) ;
float y = sinf ( theta ) ;
float rsqrt3 = .57735f ;
float nx = cosf ( theta ) * rsqrt3 ;
float ny = sinf ( theta ) * rsqrt3 ;
float nz = - rsqrt3 ;
float u = x + .5f ;
float v = .5f - y ;
vertices [ segments * 0 ] = ( ShapeVertex ) { { x , y , 0.f } , { 0.f , 0.f , 1.f } , { u , v } } ;
vertices [ segments * 1 ] = ( ShapeVertex ) { { x , y , 0.f } , { nx , ny , nz } , { u , v } } ;
vertices + + ;
}
vertices [ segments ] = ( ShapeVertex ) { { 0.f , 0.f , - 1.f } , { 0.f , 0.f , 0.f } , { .5f , .5f } } ;
// Base
for ( uint32_t i = 0 ; i < segments - 2 ; i + + ) {
uint16_t tri [ ] = { 0 , i + 1 , i + 2 } ;
memcpy ( indices , tri , sizeof ( tri ) ) ;
indices + = COUNTOF ( tri ) ;
}
// Sides
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t tri [ ] = { segments + i , segments + ( i + 1 ) % segments , vertexCount - 1 } ;
memcpy ( indices , tri , sizeof ( tri ) ) ;
indices + = COUNTOF ( tri ) ;
}
}
2022-07-09 23:38:25 +00:00
void lovrPassCapsule ( Pass * pass , float * transform , uint32_t segments ) {
float sx = vec3_length ( transform + 0 ) ;
float sy = vec3_length ( transform + 4 ) ;
float sz = vec3_length ( transform + 8 ) ;
vec3_scale ( transform + 0 , 1.f / sx ) ;
vec3_scale ( transform + 4 , 1.f / sy ) ;
vec3_scale ( transform + 8 , 1.f / sz ) ;
float radius = sx ;
float length = sz * .5f ;
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_CAPSULE , FLOAT_BITS ( radius ) , FLOAT_BITS ( length ) , segments } ;
2022-07-09 23:38:25 +00:00
uint32_t rings = segments / 2 ;
uint32_t vertexCount = 2 * ( 1 + rings * ( segments + 1 ) ) ;
uint32_t indexCount = 2 * ( 3 * segments + 6 * segments * ( rings - 1 ) ) + 6 * segments ;
ShapeVertex * vertices ;
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-07-09 23:38:25 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
2022-07-17 23:38:00 +00:00
. index . count = indexCount
2022-07-09 23:38:25 +00:00
} ) ;
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-07-09 23:38:25 +00:00
float tip = length + radius ;
uint32_t h = vertexCount / 2 ;
vertices [ 0 ] = ( ShapeVertex ) { { 0.f , 0.f , - tip } , { 0.f , 0.f , - 1.f } , { .5f , 0.f } } ;
vertices [ h ] = ( ShapeVertex ) { { 0.f , 0.f , tip } , { 0.f , 0.f , 1.f } , { .5f , 1.f } } ;
vertices + + ;
for ( uint32_t i = 1 ; i < = rings ; i + + ) {
float v = i / ( float ) rings ;
float phi = v * ( float ) M_PI / 2.f ;
float sinphi = sinf ( phi ) ;
float cosphi = cosf ( phi ) ;
for ( uint32_t j = 0 ; j < = segments ; j + + ) {
float u = j / ( float ) segments ;
float theta = u * ( float ) M_PI * 2.f ;
float sintheta = sinf ( theta ) ;
float costheta = cosf ( theta ) ;
float x = costheta * sinphi ;
float y = sintheta * sinphi ;
float z = cosphi ;
vertices [ 0 ] = ( ShapeVertex ) { { x * radius , y * radius , - ( length + z * radius ) } , { x , y , - z } , { u , v } } ;
vertices [ h ] = ( ShapeVertex ) { { x * radius , y * radius , ( length + z * radius ) } , { x , y , z } , { u , 1.f - v } } ;
vertices + + ;
}
}
uint16_t * i1 = indices ;
uint16_t * i2 = indices + ( indexCount - 6 * segments ) / 2 ;
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t wedge1 [ ] = { 0 , 0 + i + 2 , 0 + i + 1 } ;
uint16_t wedge2 [ ] = { h , h + i + 1 , h + i + 2 } ;
memcpy ( i1 , wedge1 , sizeof ( wedge1 ) ) ;
memcpy ( i2 , wedge2 , sizeof ( wedge2 ) ) ;
i1 + = COUNTOF ( wedge1 ) ;
i2 + = COUNTOF ( wedge2 ) ;
}
for ( uint32_t i = 0 ; i < rings - 1 ; i + + ) {
for ( uint32_t j = 0 ; j < segments ; j + + ) {
uint16_t a = 1 + i * ( segments + 1 ) + 0 + j ;
uint16_t b = 1 + i * ( segments + 1 ) + 1 + j ;
uint16_t c = 1 + i * ( segments + 1 ) + 0 + segments + 1 + j ;
uint16_t d = 1 + i * ( segments + 1 ) + 1 + segments + 1 + j ;
uint16_t quad1 [ ] = { a , b , c , c , b , d } ;
uint16_t quad2 [ ] = { h + a , h + c , h + b , h + b , h + c , h + d } ;
memcpy ( i1 , quad1 , sizeof ( quad1 ) ) ;
memcpy ( i2 , quad2 , sizeof ( quad2 ) ) ;
i1 + = COUNTOF ( quad1 ) ;
i2 + = COUNTOF ( quad2 ) ;
}
}
for ( uint32_t i = 0 ; i < segments ; i + + ) {
uint16_t a = h - segments - 1 + i ;
uint16_t b = h - segments - 1 + i + 1 ;
uint16_t c = vertexCount - segments - 1 + i ;
uint16_t d = vertexCount - segments - 1 + i + 1 ;
uint16_t quad [ ] = { a , b , c , c , b , d } ;
memcpy ( i2 , quad , sizeof ( quad ) ) ;
i2 + = COUNTOF ( quad ) ;
}
}
2022-06-24 02:52:37 +00:00
void lovrPassTorus ( Pass * pass , float * transform , uint32_t segmentsT , uint32_t segmentsP ) {
float sx = vec3_length ( transform + 0 ) ;
float sy = vec3_length ( transform + 4 ) ;
float sz = vec3_length ( transform + 8 ) ;
vec3_scale ( transform + 0 , 1.f / sx ) ;
vec3_scale ( transform + 4 , 1.f / sy ) ;
vec3_scale ( transform + 8 , 1.f / sz ) ;
float radius = sx * .5f ;
float thickness = sz * .5f ;
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_TORUS , FLOAT_BITS ( radius ) , FLOAT_BITS ( thickness ) , segmentsT , segmentsP } ;
2022-06-24 02:52:37 +00:00
uint32_t vertexCount = segmentsT * segmentsP ;
uint32_t indexCount = segmentsT * segmentsP * 6 ;
ShapeVertex * vertices ;
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-24 02:52:37 +00:00
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
// T and P stand for toroidal and poloidal, or theta and phi
float dt = ( 2.f * ( float ) M_PI ) / segmentsT ;
float dp = ( 2.f * ( float ) M_PI ) / segmentsP ;
for ( uint32_t t = 0 ; t < segmentsT ; t + + ) {
float theta = t * dt ;
float tx = cosf ( theta ) ;
float ty = sinf ( theta ) ;
for ( uint32_t p = 0 ; p < segmentsP ; p + + ) {
float phi = p * dp ;
float nx = cosf ( phi ) * tx ;
float ny = cosf ( phi ) * ty ;
float nz = sinf ( phi ) ;
* vertices + + = ( ShapeVertex ) {
. position = { tx * radius + nx * thickness , ty * radius + ny * thickness , nz * thickness } ,
. normal = { nx , ny , nz }
} ;
uint16_t a = ( t + 0 ) * segmentsP + p ;
uint16_t b = ( t + 1 ) % segmentsT * segmentsP + p ;
uint16_t c = ( t + 0 ) * segmentsP + ( p + 1 ) % segmentsP ;
uint16_t d = ( t + 1 ) % segmentsT * segmentsP + ( p + 1 ) % segmentsP ;
2022-07-11 01:02:49 +00:00
uint16_t quad [ ] = { a , b , c , c , b , d } ;
2022-06-24 02:52:37 +00:00
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
}
}
2022-07-18 02:53:31 +00:00
void lovrPassText ( Pass * pass , ColoredString * strings , uint32_t count , float * transform , float wrap , HorizontalAlign halign , VerticalAlign valign ) {
Font * font = pass - > pipeline - > font ? pass - > pipeline - > font : lovrGraphicsGetDefaultFont ( ) ;
2022-07-04 05:40:05 +00:00
size_t totalLength = 0 ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
totalLength + = strings [ i ] . length ;
}
2022-08-07 01:05:30 +00:00
size_t stack = tempPush ( ) ;
2022-07-04 05:40:05 +00:00
GlyphVertex * vertices = tempAlloc ( totalLength * 4 * sizeof ( GlyphVertex ) ) ;
uint32_t glyphCount ;
uint32_t lineCount ;
float leading = lovrRasterizerGetLeading ( font - > info . rasterizer ) * font - > lineSpacing ;
float ascent = lovrRasterizerGetAscent ( font - > info . rasterizer ) ;
float scale = 1.f / font - > pixelDensity ;
wrap / = scale ;
Material * material ;
2022-07-17 15:59:39 +00:00
bool flip = pass - > cameras [ 0 ] . projection [ 5 ] > 0.f ;
lovrFontGetVertices ( font , strings , count , wrap , halign , valign , vertices , & glyphCount , & lineCount , & material , flip ) ;
2022-07-04 05:40:05 +00:00
2022-06-21 01:26:15 +00:00
mat4_scale ( transform , scale , scale , scale ) ;
2022-07-17 15:59:39 +00:00
float offset = - ascent + valign / 2.f * ( leading * lineCount ) ;
mat4_translate ( transform , 0.f , flip ? - offset : offset , 0.f ) ;
2022-06-21 01:26:15 +00:00
2022-07-12 03:52:35 +00:00
GlyphVertex * vertexPointer ;
2022-06-21 01:26:15 +00:00
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-21 01:26:15 +00:00
. shader = SHADER_FONT ,
. material = font - > material ,
. transform = transform ,
. vertex . format = VERTEX_GLYPH ,
2022-07-12 03:52:35 +00:00
. vertex . pointer = ( void * * ) & vertexPointer ,
2022-07-04 05:40:05 +00:00
. vertex . count = glyphCount * 4 ,
2022-07-12 03:52:35 +00:00
. index . pointer = ( void * * ) & indices ,
. index . count = glyphCount * 6
2022-06-21 01:26:15 +00:00
} ) ;
2022-07-12 03:52:35 +00:00
memcpy ( vertexPointer , vertices , glyphCount * 4 * sizeof ( GlyphVertex ) ) ;
2022-07-04 05:40:05 +00:00
for ( uint32_t i = 0 ; i < glyphCount * 4 ; i + = 4 ) {
2022-06-21 01:26:15 +00:00
uint16_t quad [ ] = { i + 0 , i + 2 , i + 1 , i + 1 , i + 2 , i + 3 } ;
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
2022-06-30 03:17:26 +00:00
tempPop ( stack ) ;
2022-06-21 01:26:15 +00:00
}
2022-07-04 07:18:38 +00:00
void lovrPassSkybox ( Pass * pass , Texture * texture ) {
if ( texture - > info . type = = TEXTURE_2D ) {
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-08-02 05:10:06 +00:00
. shader = SHADER_EQUIRECT ,
2022-07-04 07:18:38 +00:00
. material = texture ? lovrTextureGetMaterial ( texture ) : NULL ,
. vertex . format = VERTEX_EMPTY ,
. count = 6
} ) ;
} else {
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-08-02 05:10:06 +00:00
. shader = SHADER_CUBEMAP ,
2022-07-04 07:18:38 +00:00
. material = texture ? lovrTextureGetMaterial ( texture ) : NULL ,
. vertex . format = VERTEX_EMPTY ,
. count = 6
} ) ;
}
}
2022-06-25 02:59:48 +00:00
void lovrPassFill ( Pass * pass , Texture * texture ) {
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-07-31 20:26:35 +00:00
. shader = texture - > info . layers = = 2 ? SHADER_STEREOBLIT : SHADER_FILL ,
2022-06-25 02:59:48 +00:00
. material = texture ? lovrTextureGetMaterial ( texture ) : NULL ,
. vertex . format = VERTEX_EMPTY ,
. count = 3
} ) ;
}
2022-06-22 03:05:57 +00:00
void lovrPassMonkey ( Pass * pass , float * transform ) {
2022-07-12 03:52:35 +00:00
uint32_t key [ ] = { SHAPE_MONKEY } ;
2022-06-22 03:05:57 +00:00
uint32_t vertexCount = COUNTOF ( monkey_vertices ) / 6 ;
ShapeVertex * vertices ;
2022-07-12 03:52:35 +00:00
uint16_t * indices ;
2022-06-22 03:05:57 +00:00
lovrPassDraw ( pass , & ( Draw ) {
2022-07-12 03:52:35 +00:00
. hash = hash64 ( key , sizeof ( key ) ) ,
2022-07-13 02:35:23 +00:00
. mode = MESH_TRIANGLES ,
2022-06-22 03:05:57 +00:00
. vertex . pointer = ( void * * ) & vertices ,
2022-07-12 03:52:35 +00:00
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
2022-06-22 03:05:57 +00:00
. index . count = COUNTOF ( monkey_indices ) ,
. transform = transform
} ) ;
2022-07-12 03:52:35 +00:00
if ( ! vertices ) {
return ;
}
2022-06-22 03:05:57 +00:00
// Manual vertex format conversion to avoid another format (and sn8x3 isn't always supported)
for ( uint32_t i = 0 ; i < vertexCount ; i + + ) {
vertices [ i ] = ( ShapeVertex ) {
. position . x = monkey_vertices [ 6 * i + 0 ] / 255.f * monkey_size [ 0 ] + monkey_offset [ 0 ] ,
. position . y = monkey_vertices [ 6 * i + 1 ] / 255.f * monkey_size [ 1 ] + monkey_offset [ 1 ] ,
. position . z = monkey_vertices [ 6 * i + 2 ] / 255.f * monkey_size [ 2 ] + monkey_offset [ 2 ] ,
. normal . x = monkey_vertices [ 6 * i + 3 ] / 255.f * 2.f - 1.f ,
. normal . y = monkey_vertices [ 6 * i + 4 ] / 255.f * 2.f - 1.f ,
. normal . z = monkey_vertices [ 6 * i + 5 ] / 255.f * 2.f - 1.f ,
} ;
}
2022-07-12 03:52:35 +00:00
memcpy ( indices , monkey_indices , sizeof ( monkey_indices ) ) ;
2022-06-22 03:05:57 +00:00
}
2022-07-04 00:26:31 +00:00
static void renderNode ( Pass * pass , Model * model , uint32_t index , bool recurse , uint32_t instances ) {
ModelNode * node = & model - > info . data - > nodes [ index ] ;
mat4 globalTransform = model - > globalTransforms + 16 * index ;
for ( uint32_t i = 0 ; i < node - > primitiveCount ; i + + ) {
Draw draw = model - > draws [ node - > primitiveIndex + i ] ;
2022-07-04 02:20:30 +00:00
if ( node - > skin = = ~ 0u ) draw . transform = globalTransform ;
2022-07-04 00:26:31 +00:00
draw . instances = instances ;
lovrPassDraw ( pass , & draw ) ;
}
if ( recurse ) {
for ( uint32_t i = 0 ; i < node - > childCount ; i + + ) {
renderNode ( pass , model , node - > children [ i ] , true , instances ) ;
}
}
}
void lovrPassDrawModel ( Pass * pass , Model * model , float * transform , uint32_t node , bool recurse , uint32_t instances ) {
if ( model - > transformsDirty ) {
updateModelTransforms ( model , model - > info . data - > rootNode , ( float [ ] ) MAT4_IDENTITY ) ;
lovrModelReskin ( model ) ;
model - > transformsDirty = false ;
}
if ( node = = ~ 0u ) {
node = model - > info . data - > rootNode ;
}
lovrPassPush ( pass , STACK_TRANSFORM ) ;
lovrPassTransform ( pass , transform ) ;
renderNode ( pass , model , node , recurse , instances ) ;
lovrPassPop ( pass , STACK_TRANSFORM ) ;
}
2022-06-10 06:05:32 +00:00
void lovrPassMesh ( Pass * pass , Buffer * vertices , Buffer * indices , float * transform , uint32_t start , uint32_t count , uint32_t instances ) {
if ( count = = ~ 0u ) {
2022-07-10 01:30:36 +00:00
if ( indices | | vertices ) {
count = ( indices ? indices : vertices ) - > info . length - start ;
} else {
count = 0 ;
}
2022-06-10 06:05:32 +00:00
}
if ( indices ) {
lovrCheck ( count < = indices - > info . length - start , " Mesh draw range exceeds index buffer size " ) ;
2022-07-10 01:30:36 +00:00
} else if ( vertices ) {
2022-06-10 06:05:32 +00:00
lovrCheck ( count < = vertices - > info . length - start , " Mesh draw range exceeds vertex buffer size " ) ;
}
lovrPassDraw ( pass , & ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = pass - > pipeline - > mode ,
2022-06-10 06:05:32 +00:00
. vertex . buffer = vertices ,
. index . buffer = indices ,
. transform = transform ,
. start = start ,
. count = count ,
. instances = instances
} ) ;
}
2022-08-06 05:11:06 +00:00
void lovrPassMeshIndirect ( Pass * pass , Buffer * vertices , Buffer * indices , Buffer * draws , uint32_t count , uint32_t offset , uint32_t stride ) {
2022-06-10 06:38:33 +00:00
lovrCheck ( pass - > info . type = = PASS_RENDER , " This function can only be called on a render pass " ) ;
2022-08-06 05:11:06 +00:00
lovrCheck ( offset % 4 = = 0 , " Buffer offset must be a multiple of 4 when sourcing draws from a Buffer " ) ;
2022-06-10 06:38:33 +00:00
uint32_t commandSize = indices ? 20 : 16 ;
stride = stride ? stride : commandSize ;
uint32_t totalSize = stride * ( count - 1 ) + commandSize ;
2022-08-06 05:11:06 +00:00
lovrCheck ( offset + totalSize < draws - > size , " Draw buffer range exceeds the size of the buffer " ) ;
2022-06-10 06:38:33 +00:00
Draw draw = ( Draw ) {
2022-07-13 02:35:23 +00:00
. mode = pass - > pipeline - > mode ,
2022-06-10 06:38:33 +00:00
. vertex . buffer = vertices ,
. index . buffer = indices
} ;
Shader * shader = pass - > pipeline - > shader ;
2022-08-06 05:11:06 +00:00
lovrCheck ( shader , " A custom Shader must be bound to source draws from a Buffer " ) ;
2022-06-10 06:38:33 +00:00
2022-08-09 03:36:22 +00:00
bindPipeline ( pass , & draw , shader ) ;
bindBundles ( pass , & draw , shader ) ;
bindBuffers ( pass , & draw ) ;
pushConstants ( pass , shader ) ;
2022-06-10 06:38:33 +00:00
if ( indices ) {
gpu_draw_indirect_indexed ( pass - > stream , draws - > gpu , offset , count , stride ) ;
} else {
gpu_draw_indirect ( pass - > stream , draws - > gpu , offset , count , stride ) ;
}
trackBuffer ( pass , draws , GPU_PHASE_INDIRECT , GPU_CACHE_INDIRECT ) ;
}
2022-06-04 18:54:05 +00:00
void lovrPassCompute ( Pass * pass , uint32_t x , uint32_t y , uint32_t z , Buffer * indirect , uint32_t offset ) {
lovrCheck ( pass - > info . type = = PASS_COMPUTE , " This function can only be called on a compute pass " ) ;
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader & & shader - > info . type = = SHADER_COMPUTE , " Tried to run a compute shader, but no compute shader is bound " ) ;
2022-08-06 20:06:42 +00:00
lovrCheck ( x < = state . limits . workgroupCount [ 0 ] , " Compute %s count exceeds workgroupCount limit " , " x " ) ;
lovrCheck ( y < = state . limits . workgroupCount [ 1 ] , " Compute %s count exceeds workgroupCount limit " , " y " ) ;
lovrCheck ( z < = state . limits . workgroupCount [ 2 ] , " Compute %s count exceeds workgroupCount limit " , " z " ) ;
2022-06-04 18:54:05 +00:00
2022-08-07 01:05:30 +00:00
gpu_pipeline * pipeline = state . pipelines . data [ shader - > computePipelineIndex ] ;
2022-06-04 18:54:05 +00:00
if ( pass - > pipeline - > dirty ) {
gpu_bind_pipeline ( pass - > stream , pipeline , true ) ;
pass - > pipeline - > dirty = false ;
}
2022-08-09 03:36:22 +00:00
bindBundles ( pass , NULL , shader ) ;
pushConstants ( pass , shader ) ;
2022-06-04 18:54:05 +00:00
if ( indirect ) {
lovrCheck ( offset % 4 = = 0 , " Indirect compute offset must be a multiple of 4 " ) ;
lovrCheck ( offset < = indirect - > size - 12 , " Indirect compute offset overflows the Buffer " ) ;
2022-06-04 21:28:23 +00:00
trackBuffer ( pass , indirect , GPU_PHASE_INDIRECT , GPU_CACHE_INDIRECT ) ;
2022-06-04 18:54:05 +00:00
gpu_compute_indirect ( pass - > stream , indirect - > gpu , offset ) ;
} else {
gpu_compute ( pass - > stream , x , y , z ) ;
}
}
2022-05-26 02:45:01 +00:00
void lovrPassClearBuffer ( Pass * pass , Buffer * buffer , uint32_t offset , uint32_t extent ) {
if ( extent = = 0 ) return ;
if ( extent = = ~ 0u ) extent = buffer - > size - offset ;
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! lovrBufferIsTemporary ( buffer ) , " Temporary buffers can not be cleared " ) ;
lovrCheck ( offset % 4 = = 0 , " Buffer clear offset must be a multiple of 4 " ) ;
lovrCheck ( extent % 4 = = 0 , " Buffer clear extent must be a multiple of 4 " ) ;
lovrCheck ( offset + extent < = buffer - > size , " Buffer clear range goes past the end of the Buffer " ) ;
gpu_clear_buffer ( pass - > stream , buffer - > gpu , offset , extent ) ;
2022-06-12 02:07:46 +00:00
trackBuffer ( pass , buffer , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 02:45:01 +00:00
}
void lovrPassClearTexture ( Pass * pass , Texture * texture , float value [ 4 ] , uint32_t layer , uint32_t layerCount , uint32_t level , uint32_t levelCount ) {
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! texture - > info . parent , " Texture views can not be cleared " ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( texture - > info . usage & TEXTURE_TRANSFER , " Texture must be created with 'transfer' usage to clear it " ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( texture - > info . type = = TEXTURE_3D | | layer + layerCount < = texture - > info . layers , " Texture clear range exceeds texture layer count " ) ;
2022-05-26 02:45:01 +00:00
lovrCheck ( level + levelCount < = texture - > info . mipmaps , " Texture clear range exceeds texture mipmap count " ) ;
gpu_clear_texture ( pass - > stream , texture - > gpu , value , layer , layerCount , level , levelCount ) ;
2022-06-12 02:07:46 +00:00
trackTexture ( pass , texture , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 02:45:01 +00:00
}
2022-06-22 07:39:56 +00:00
void * lovrPassCopyDataToBuffer ( Pass * pass , Buffer * buffer , uint32_t offset , uint32_t extent ) {
2022-05-26 06:52:24 +00:00
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! lovrBufferIsTemporary ( buffer ) , " Temporary buffers can not be copied to, use Buffer:setData " ) ;
lovrCheck ( offset + extent < = buffer - > size , " Buffer copy range goes past the end of the Buffer " ) ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
void * pointer = gpu_map ( scratchpad , extent , 4 , GPU_MAP_WRITE ) ;
gpu_copy_buffers ( pass - > stream , scratchpad , buffer - > gpu , 0 , offset , extent ) ;
2022-06-12 02:07:46 +00:00
trackBuffer ( pass , buffer , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-06-22 07:39:56 +00:00
return pointer ;
2022-05-26 06:52:24 +00:00
}
void lovrPassCopyBufferToBuffer ( Pass * pass , Buffer * src , Buffer * dst , uint32_t srcOffset , uint32_t dstOffset , uint32_t extent ) {
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! lovrBufferIsTemporary ( dst ) , " Temporary buffers can not be copied to " ) ;
lovrCheck ( srcOffset + extent < = src - > size , " Buffer copy range goes past the end of the source Buffer " ) ;
lovrCheck ( dstOffset + extent < = dst - > size , " Buffer copy range goes past the end of the destination Buffer " ) ;
gpu_copy_buffers ( pass - > stream , src - > gpu , dst - > gpu , srcOffset , dstOffset , extent ) ;
2022-06-12 02:07:46 +00:00
trackBuffer ( pass , src , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ ) ;
trackBuffer ( pass , dst , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 06:52:24 +00:00
}
2022-06-28 06:14:26 +00:00
void lovrPassCopyTallyToBuffer ( Pass * pass , Tally * tally , Buffer * buffer , uint32_t srcIndex , uint32_t dstOffset , uint32_t count ) {
2022-07-01 01:51:03 +00:00
if ( count = = ~ 0u ) count = tally - > info . count ;
2022-06-28 06:14:26 +00:00
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! lovrBufferIsTemporary ( buffer ) , " Temporary buffers can not be copied to " ) ;
lovrCheck ( srcIndex + count < = tally - > info . count , " Tally copy range exceeds the number of slots in the Tally " ) ;
lovrCheck ( dstOffset + count * 4 < = buffer - > size , " Buffer copy range goes past the end of the destination Buffer " ) ;
lovrCheck ( dstOffset % 4 = = 0 , " Buffer copy offset must be a multiple of 4 " ) ;
2022-07-01 01:51:03 +00:00
2022-07-17 20:17:33 +00:00
if ( tally - > info . type = = TALLY_TIME ) {
2022-07-14 07:05:58 +00:00
lovrTallyResolve ( tally , srcIndex , count , buffer - > gpu , dstOffset , pass - > stream ) ;
2022-06-28 06:14:26 +00:00
trackBuffer ( pass , buffer , GPU_PHASE_SHADER_COMPUTE , GPU_CACHE_STORAGE_WRITE ) ;
} else {
2022-07-17 18:38:55 +00:00
uint32_t stride = tally - > info . type = = TALLY_SHADER ? 16 : 4 ;
2022-07-14 07:05:58 +00:00
gpu_copy_tally_buffer ( pass - > stream , tally - > gpu , buffer - > gpu , srcIndex , dstOffset , count , stride ) ;
2022-06-28 06:14:26 +00:00
trackBuffer ( pass , buffer , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
}
}
2022-05-26 06:52:24 +00:00
void lovrPassCopyImageToTexture ( Pass * pass , Image * image , Texture * texture , uint32_t srcOffset [ 4 ] , uint32_t dstOffset [ 4 ] , uint32_t extent [ 4 ] ) {
if ( extent [ 0 ] = = ~ 0u ) extent [ 0 ] = MIN ( texture - > info . width - dstOffset [ 0 ] , lovrImageGetWidth ( image , srcOffset [ 3 ] ) - srcOffset [ 0 ] ) ;
if ( extent [ 1 ] = = ~ 0u ) extent [ 1 ] = MIN ( texture - > info . height - dstOffset [ 1 ] , lovrImageGetHeight ( image , srcOffset [ 3 ] ) - srcOffset [ 1 ] ) ;
2022-07-30 22:08:30 +00:00
if ( extent [ 2 ] = = ~ 0u ) extent [ 2 ] = MIN ( texture - > info . layers - dstOffset [ 2 ] , lovrImageGetLayerCount ( image ) - srcOffset [ 2 ] ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( texture - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to copy to it " ) ;
lovrCheck ( ! texture - > info . parent , " Texture views can not be written to " ) ;
lovrCheck ( texture - > info . samples = = 1 , " Multisampled Textures can not be written to " ) ;
lovrCheck ( lovrImageGetFormat ( image ) = = texture - > info . format , " Image and Texture formats must match " ) ;
lovrCheck ( srcOffset [ 0 ] + extent [ 0 ] < = lovrImageGetWidth ( image , srcOffset [ 3 ] ) , " Image copy region exceeds its %s " , " width " ) ;
lovrCheck ( srcOffset [ 1 ] + extent [ 1 ] < = lovrImageGetHeight ( image , srcOffset [ 3 ] ) , " Image copy region exceeds its %s " , " height " ) ;
lovrCheck ( srcOffset [ 2 ] + extent [ 2 ] < = lovrImageGetLayerCount ( image ) , " Image copy region exceeds its %s " , " layer count " ) ;
lovrCheck ( srcOffset [ 3 ] < lovrImageGetLevelCount ( image ) , " Image copy region exceeds its %s " , " mipmap count " ) ;
checkTextureBounds ( & texture - > info , dstOffset , extent ) ;
2022-08-07 01:05:30 +00:00
uint32_t rowSize = measureTexture ( texture - > info . format , extent [ 0 ] , 1 , 1 ) ;
uint32_t totalSize = measureTexture ( texture - > info . format , extent [ 0 ] , extent [ 1 ] , 1 ) * extent [ 2 ] ;
uint32_t layerOffset = measureTexture ( texture - > info . format , extent [ 0 ] , srcOffset [ 1 ] , 1 ) ;
2022-05-26 06:52:24 +00:00
layerOffset + = measureTexture ( texture - > info . format , srcOffset [ 0 ] , 1 , 1 ) ;
2022-08-07 01:05:30 +00:00
uint32_t pitch = measureTexture ( texture - > info . format , lovrImageGetWidth ( image , srcOffset [ 3 ] ) , 1 , 1 ) ;
2022-05-26 06:52:24 +00:00
gpu_buffer * buffer = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
char * dst = gpu_map ( buffer , totalSize , 64 , GPU_MAP_WRITE ) ;
for ( uint32_t z = 0 ; z < extent [ 2 ] ; z + + ) {
const char * src = ( char * ) lovrImageGetLayerData ( image , srcOffset [ 3 ] , z ) + layerOffset ;
for ( uint32_t y = 0 ; y < extent [ 1 ] ; y + + ) {
memcpy ( dst , src , rowSize ) ;
dst + = rowSize ;
src + = pitch ;
}
}
gpu_copy_buffer_texture ( pass - > stream , buffer , texture - > gpu , 0 , dstOffset , extent ) ;
2022-06-12 02:07:46 +00:00
trackTexture ( pass , texture , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 06:52:24 +00:00
}
void lovrPassCopyTextureToTexture ( Pass * pass , Texture * src , Texture * dst , uint32_t srcOffset [ 4 ] , uint32_t dstOffset [ 4 ] , uint32_t extent [ 3 ] ) {
if ( extent [ 0 ] = = ~ 0u ) extent [ 0 ] = MIN ( src - > info . width - srcOffset [ 0 ] , dst - > info . width - dstOffset [ 0 ] ) ;
if ( extent [ 1 ] = = ~ 0u ) extent [ 1 ] = MIN ( src - > info . height - srcOffset [ 1 ] , dst - > info . height - dstOffset [ 0 ] ) ;
2022-07-30 22:08:30 +00:00
if ( extent [ 2 ] = = ~ 0u ) extent [ 2 ] = MIN ( src - > info . layers - srcOffset [ 2 ] , dst - > info . layers - dstOffset [ 0 ] ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( src - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to copy %s it " , " from " ) ;
lovrCheck ( dst - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to copy %s it " , " to " ) ;
lovrCheck ( ! src - > info . parent & & ! dst - > info . parent , " Can not copy texture views " ) ;
lovrCheck ( src - > info . format = = dst - > info . format , " Copying between Textures requires them to have the same format " ) ;
lovrCheck ( src - > info . samples = = dst - > info . samples , " Texture sample counts must match to copy between them " ) ;
checkTextureBounds ( & src - > info , srcOffset , extent ) ;
checkTextureBounds ( & dst - > info , dstOffset , extent ) ;
gpu_copy_textures ( pass - > stream , src - > gpu , dst - > gpu , srcOffset , dstOffset , extent ) ;
2022-06-12 02:07:46 +00:00
trackTexture ( pass , src , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ ) ;
trackTexture ( pass , dst , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 06:52:24 +00:00
}
void lovrPassBlit ( Pass * pass , Texture * src , Texture * dst , uint32_t srcOffset [ 4 ] , uint32_t dstOffset [ 4 ] , uint32_t srcExtent [ 3 ] , uint32_t dstExtent [ 3 ] , FilterMode filter ) {
if ( srcExtent [ 0 ] = = ~ 0u ) srcExtent [ 0 ] = src - > info . width - srcOffset [ 0 ] ;
if ( srcExtent [ 1 ] = = ~ 0u ) srcExtent [ 1 ] = src - > info . height - srcOffset [ 1 ] ;
2022-07-30 22:08:30 +00:00
if ( srcExtent [ 2 ] = = ~ 0u ) srcExtent [ 2 ] = src - > info . layers - srcOffset [ 2 ] ;
2022-05-26 06:52:24 +00:00
if ( dstExtent [ 0 ] = = ~ 0u ) dstExtent [ 0 ] = dst - > info . width - dstOffset [ 0 ] ;
if ( dstExtent [ 1 ] = = ~ 0u ) dstExtent [ 1 ] = dst - > info . height - dstOffset [ 1 ] ;
2022-07-30 22:08:30 +00:00
if ( dstExtent [ 2 ] = = ~ 0u ) dstExtent [ 2 ] = dst - > info . layers - dstOffset [ 2 ] ;
2022-05-26 06:52:24 +00:00
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! src - > info . parent & & ! dst - > info . parent , " Can not blit Texture views " ) ;
lovrCheck ( src - > info . samples = = 1 & & dst - > info . samples = = 1 , " Multisampled textures can not be used for blits " ) ;
2022-05-26 07:07:10 +00:00
lovrCheck ( src - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to blit %s it " , " from " ) ;
lovrCheck ( dst - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to blit %s it " , " to " ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( state . features . formats [ src - > info . format ] & GPU_FEATURE_BLIT_SRC , " This GPU does not support blitting from the source texture's format " ) ;
lovrCheck ( state . features . formats [ dst - > info . format ] & GPU_FEATURE_BLIT_DST , " This GPU does not support blitting to the destination texture's format " ) ;
lovrCheck ( src - > info . format = = dst - > info . format , " Texture formats must match to blit between them " ) ;
// FIXME if src or dst is 3D you can only blit 1 layer or something!
checkTextureBounds ( & src - > info , srcOffset , srcExtent ) ;
checkTextureBounds ( & dst - > info , dstOffset , dstExtent ) ;
gpu_blit ( pass - > stream , src - > gpu , dst - > gpu , srcOffset , dstOffset , srcExtent , dstExtent , ( gpu_filter ) filter ) ;
2022-06-12 02:07:46 +00:00
trackTexture ( pass , src , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ ) ;
trackTexture ( pass , dst , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 06:52:24 +00:00
}
2022-05-26 07:07:10 +00:00
void lovrPassMipmap ( Pass * pass , Texture * texture , uint32_t base , uint32_t count ) {
if ( count = = ~ 0u ) count = texture - > info . mipmaps - ( base + 1 ) ;
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! texture - > info . parent , " Can not mipmap a Texture view " ) ;
lovrCheck ( texture - > info . samples = = 1 , " Can not mipmap a multisampled texture " ) ;
2022-07-14 07:05:58 +00:00
lovrCheck ( texture - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to mipmap it " ) ;
2022-05-26 07:07:10 +00:00
lovrCheck ( state . features . formats [ texture - > info . format ] & GPU_FEATURE_BLIT_SRC , " This GPU does not support blitting %s the source texture's format, which is required for mipmapping " , " from " ) ;
lovrCheck ( state . features . formats [ texture - > info . format ] & GPU_FEATURE_BLIT_DST , " This GPU does not support blitting %s the source texture's format, which is required for mipmapping " , " to " ) ;
lovrCheck ( base + count < texture - > info . mipmaps , " Trying to generate too many mipmaps " ) ;
2022-06-23 02:05:36 +00:00
mipmapTexture ( pass - > stream , texture , base , count ) ;
2022-06-12 02:07:46 +00:00
trackTexture ( pass , texture , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ | GPU_CACHE_TRANSFER_WRITE ) ;
2022-05-26 07:07:10 +00:00
}
2022-07-14 07:05:58 +00:00
Readback * lovrPassReadBuffer ( Pass * pass , Buffer * buffer , uint32_t offset , uint32_t extent ) {
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! lovrBufferIsTemporary ( buffer ) , " Unable to read back a temporary buffer " ) ;
lovrCheck ( offset + extent < = buffer - > size , " Tried to read past the end of the Buffer " ) ;
2022-07-15 02:23:02 +00:00
Readback * readback = lovrReadbackCreate ( & ( ReadbackInfo ) {
. type = READBACK_BUFFER ,
. buffer . object = buffer ,
. buffer . offset = offset ,
. buffer . extent = extent
} ) ;
2022-07-14 07:05:58 +00:00
gpu_copy_buffers ( pass - > stream , buffer - > gpu , readback - > buffer , offset , 0 , extent ) ;
trackBuffer ( pass , buffer , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ ) ;
2022-07-31 20:02:41 +00:00
arr_push ( & pass - > readbacks , readback ) ;
2022-07-14 07:05:58 +00:00
return readback ;
}
Readback * lovrPassReadTexture ( Pass * pass , Texture * texture , uint32_t offset [ 4 ] , uint32_t extent [ 3 ] ) {
if ( extent [ 0 ] = = ~ 0u ) extent [ 0 ] = texture - > info . width - offset [ 0 ] ;
if ( extent [ 1 ] = = ~ 0u ) extent [ 1 ] = texture - > info . height - offset [ 1 ] ;
lovrCheck ( extent [ 2 ] = = 1 , " Currently, only one layer can be read from a Texture " ) ;
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( ! texture - > info . parent , " Can not read from a Texture view " ) ;
lovrCheck ( texture - > info . samples = = 1 , " Can not read from a multisampled texture " ) ;
lovrCheck ( texture - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to read from it " ) ;
checkTextureBounds ( & texture - > info , offset , extent ) ;
Readback * readback = lovrReadbackCreate ( & ( ReadbackInfo ) {
2022-07-15 02:23:02 +00:00
. type = READBACK_TEXTURE ,
. texture . object = texture ,
. texture . offset = { offset [ 0 ] , offset [ 1 ] , offset [ 2 ] , offset [ 3 ] } ,
. texture . extent = { extent [ 0 ] , extent [ 1 ] }
2022-07-14 07:05:58 +00:00
} ) ;
gpu_copy_texture_buffer ( pass - > stream , texture - > gpu , readback - > buffer , offset , 0 , extent ) ;
trackTexture ( pass , texture , GPU_PHASE_TRANSFER , GPU_CACHE_TRANSFER_READ ) ;
2022-07-31 20:02:41 +00:00
arr_push ( & pass - > readbacks , readback ) ;
2022-07-14 07:05:58 +00:00
return readback ;
}
Readback * lovrPassReadTally ( Pass * pass , Tally * tally , uint32_t index , uint32_t count ) {
lovrCheck ( pass - > info . type = = PASS_TRANSFER , " This function can only be called on a transfer pass " ) ;
lovrCheck ( index + count < = tally - > info . count , " Tally read range exceeds the number of slots in the Tally " ) ;
2022-07-15 02:23:02 +00:00
Readback * readback = lovrReadbackCreate ( & ( ReadbackInfo ) {
. type = READBACK_TALLY ,
. tally . object = tally ,
. tally . index = index ,
. tally . count = count
} ) ;
2022-07-14 07:05:58 +00:00
2022-07-17 20:17:33 +00:00
if ( tally - > info . type = = TALLY_TIME ) {
2022-07-14 07:05:58 +00:00
lovrTallyResolve ( tally , index , count , readback - > buffer , 0 , pass - > stream ) ;
} else {
2022-07-17 18:38:55 +00:00
uint32_t stride = tally - > info . type = = TALLY_SHADER ? 16 : 4 ;
2022-07-14 07:05:58 +00:00
gpu_copy_tally_buffer ( pass - > stream , tally - > gpu , readback - > buffer , index , 0 , count , stride ) ;
}
2022-07-31 20:02:41 +00:00
arr_push ( & pass - > readbacks , readback ) ;
2022-07-14 07:05:58 +00:00
return readback ;
}
2022-06-25 02:00:59 +00:00
void lovrPassTick ( Pass * pass , Tally * tally , uint32_t index ) {
2022-07-15 02:23:02 +00:00
lovrCheck ( tally - > info . views = = pass - > cameraCount , " Tally view count does not match Pass view count " ) ;
2022-06-25 02:00:59 +00:00
lovrCheck ( index < tally - > info . count , " Trying to use tally slot #%d, but the tally only has %d slots " , index + 1 , tally - > info . count ) ;
if ( tally - > tick ! = state . tick ) {
2022-07-17 20:17:33 +00:00
uint32_t multiplier = tally - > info . type = = TALLY_TIME ? 2 * tally - > info . count * tally - > info . views : 1 ;
2022-07-15 02:23:02 +00:00
gpu_clear_tally ( state . stream , tally - > gpu , 0 , tally - > info . count * multiplier ) ;
2022-06-25 02:00:59 +00:00
tally - > tick = state . tick ;
}
2022-07-17 20:17:33 +00:00
if ( tally - > info . type = = TALLY_TIME ) {
2022-06-25 02:00:59 +00:00
gpu_tally_mark ( pass - > stream , tally - > gpu , index * 2 * tally - > info . views ) ;
} else {
gpu_tally_begin ( pass - > stream , tally - > gpu , index ) ;
}
}
void lovrPassTock ( Pass * pass , Tally * tally , uint32_t index ) {
2022-07-15 02:23:02 +00:00
lovrCheck ( tally - > info . views = = pass - > cameraCount , " Tally view count does not match Pass view count " ) ;
2022-06-25 02:00:59 +00:00
lovrCheck ( index < tally - > info . count , " Trying to use tally slot #%d, but the tally only has %d slots " , index + 1 , tally - > info . count ) ;
2022-07-17 20:17:33 +00:00
if ( tally - > info . type = = TALLY_TIME ) {
2022-06-25 02:00:59 +00:00
gpu_tally_mark ( pass - > stream , tally - > gpu , index * 2 * tally - > info . views + tally - > info . views ) ;
} else {
gpu_tally_end ( pass - > stream , tally - > gpu , index ) ;
}
}
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-29 05:30:31 +00:00
static void * tempAlloc ( size_t size ) {
2022-04-27 05:44:44 +00:00
while ( state . allocator . cursor + size > state . allocator . length ) {
lovrAssert ( state . allocator . length < < 1 < = MAX_FRAME_MEMORY , " Out of memory " ) ;
os_vm_commit ( state . allocator . memory + state . allocator . length , state . allocator . length ) ;
state . allocator . length < < = 1 ;
}
uint32_t cursor = ALIGN ( state . allocator . cursor , 8 ) ;
state . allocator . cursor = cursor + size ;
return state . allocator . memory + cursor ;
}
2022-08-07 01:05:30 +00:00
static size_t tempPush ( void ) {
2022-06-27 03:52:59 +00:00
return state . allocator . cursor ;
}
2022-08-07 01:05:30 +00:00
static void tempPop ( size_t stack ) {
2022-06-27 03:52:59 +00:00
state . allocator . cursor = stack ;
}
2022-07-04 00:26:31 +00:00
static int u64cmp ( const void * a , const void * b ) {
uint64_t x = * ( uint64_t * ) a , y = * ( uint64_t * ) b ;
return ( x > y ) - ( x < y ) ;
}
2022-04-29 05:30:31 +00:00
static void beginFrame ( void ) {
if ( state . active ) {
return ;
}
state . active = true ;
state . tick = gpu_begin ( ) ;
2022-08-03 05:00:11 +00:00
state . stream = gpu_stream_begin ( ) ;
2022-06-19 00:43:12 +00:00
state . allocator . cursor = 0 ;
2022-07-14 07:05:58 +00:00
processReadbacks ( ) ;
2022-04-29 05:30:31 +00:00
}
2022-07-14 07:05:58 +00:00
static void processReadbacks ( void ) {
while ( state . oldestReadback & & gpu_is_complete ( state . oldestReadback - > tick ) ) {
Readback * readback = state . oldestReadback ;
if ( readback - > image ) {
size_t size = lovrImageGetLayerSize ( readback - > image , 0 ) ;
void * data = lovrImageGetLayerData ( readback - > image , 0 , 0 ) ;
memcpy ( data , readback - > pointer , size ) ;
} else {
memcpy ( readback - > data , readback - > pointer , readback - > size ) ;
}
Readback * next = readback - > next ;
lovrRelease ( readback , lovrReadbackDestroy ) ;
state . oldestReadback = next ;
}
if ( ! state . oldestReadback ) {
state . newestReadback = NULL ;
}
}
2022-08-07 01:05:30 +00:00
static size_t getLayout ( gpu_slot * slots , uint32_t count ) {
2022-05-24 04:40:57 +00:00
uint64_t hash = hash64 ( slots , count * sizeof ( gpu_slot ) ) ;
2022-08-07 01:05:30 +00:00
size_t index ;
for ( size_t index = 0 ; index < state . layouts . length ; index + + ) {
2022-05-24 04:40:57 +00:00
if ( state . layouts . data [ index ] . hash = = hash ) {
return index ;
}
}
gpu_layout_info info = {
. slots = slots ,
. count = count
} ;
gpu_layout * handle = malloc ( gpu_sizeof_layout ( ) ) ;
lovrAssert ( handle , " Out of memory " ) ;
gpu_layout_init ( handle , & info ) ;
Layout layout = {
. hash = hash ,
. gpu = handle
} ;
index = state . layouts . length ;
arr_push ( & state . layouts , layout ) ;
return index ;
}
2022-08-07 01:05:30 +00:00
static gpu_bundle * getBundle ( size_t layoutIndex ) {
2022-05-30 19:17:17 +00:00
Layout * layout = & state . layouts . data [ layoutIndex ] ;
BundlePool * pool = layout - > head ;
const uint32_t POOL_SIZE = 512 ;
if ( pool ) {
if ( pool - > cursor < POOL_SIZE ) {
return ( gpu_bundle * ) ( ( char * ) pool - > bundles + gpu_sizeof_bundle ( ) * pool - > cursor + + ) ;
}
// If the pool's closed, move it to the end of the list and try to use the next pool
layout - > tail - > next = pool ;
layout - > tail = pool ;
layout - > head = pool - > next ;
pool - > next = NULL ;
pool - > tick = state . tick ;
pool = layout - > head ;
2022-07-14 07:05:58 +00:00
if ( pool & & gpu_is_complete ( pool - > tick ) ) {
2022-05-30 19:17:17 +00:00
pool - > cursor = 1 ;
return pool - > bundles ;
}
}
// If no pool was available, make a new one
pool = malloc ( sizeof ( BundlePool ) ) ;
gpu_bundle_pool * gpu = malloc ( gpu_sizeof_bundle_pool ( ) ) ;
gpu_bundle * bundles = malloc ( POOL_SIZE * gpu_sizeof_bundle ( ) ) ;
lovrAssert ( pool & & gpu & & bundles , " Out of memory " ) ;
pool - > gpu = gpu ;
pool - > bundles = bundles ;
2022-05-30 22:06:57 +00:00
pool - > cursor = 1 ;
2022-05-30 19:17:17 +00:00
pool - > next = layout - > head ;
gpu_bundle_pool_info info = {
. bundles = pool - > bundles ,
. layout = layout - > gpu ,
. count = POOL_SIZE
} ;
gpu_bundle_pool_init ( pool - > gpu , & info ) ;
layout - > head = pool ;
if ( ! layout - > tail ) layout - > tail = pool ;
return pool - > bundles ;
}
2022-08-03 05:00:11 +00:00
uint32_t convertTextureUsage ( uint32_t usage ) {
return
( ( usage & TEXTURE_SAMPLE ) ? GPU_TEXTURE_SAMPLE : 0 ) |
( ( usage & TEXTURE_RENDER ) ? GPU_TEXTURE_RENDER : 0 ) |
( ( usage & TEXTURE_STORAGE ) ? GPU_TEXTURE_STORAGE : 0 ) |
( ( usage & TEXTURE_TRANSFER ) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0 ) |
( ( usage = = TEXTURE_RENDER ) ? GPU_TEXTURE_TRANSIENT : 0 ) ;
2022-05-11 19:51:13 +00:00
}
2022-04-30 03:38:34 +00:00
// Returns number of bytes of a 3D texture region of a given format
2022-08-07 01:05:30 +00:00
static uint32_t measureTexture ( TextureFormat format , uint32_t w , uint32_t h , uint32_t d ) {
2022-04-30 03:38:34 +00:00
switch ( format ) {
case FORMAT_R8 : return w * h * d ;
case FORMAT_RG8 :
case FORMAT_R16 :
case FORMAT_R16F :
case FORMAT_RGB565 :
case FORMAT_RGB5A1 :
case FORMAT_D16 : return w * h * d * 2 ;
case FORMAT_RGBA8 :
case FORMAT_RG16 :
case FORMAT_RG16F :
case FORMAT_R32F :
case FORMAT_RG11B10F :
case FORMAT_RGB10A2 :
case FORMAT_D24S8 :
case FORMAT_D32F : return w * h * d * 4 ;
2022-07-17 18:03:00 +00:00
case FORMAT_D32FS8 : return w * h * d * 5 ;
2022-04-30 03:38:34 +00:00
case FORMAT_RGBA16 :
case FORMAT_RGBA16F :
case FORMAT_RG32F : return w * h * d * 8 ;
case FORMAT_RGBA32F : return w * h * d * 16 ;
case FORMAT_BC1 :
case FORMAT_BC2 :
case FORMAT_BC3 :
case FORMAT_BC4U :
case FORMAT_BC4S :
case FORMAT_BC5U :
case FORMAT_BC5S :
case FORMAT_BC6UF :
case FORMAT_BC6SF :
case FORMAT_BC7 :
case FORMAT_ASTC_4x4 : return ( ( w + 3 ) / 4 ) * ( ( h + 3 ) / 4 ) * d * 16 ;
case FORMAT_ASTC_5x4 : return ( ( w + 4 ) / 5 ) * ( ( h + 3 ) / 4 ) * d * 16 ;
case FORMAT_ASTC_5x5 : return ( ( w + 4 ) / 5 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_6x5 : return ( ( w + 5 ) / 6 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_6x6 : return ( ( w + 5 ) / 6 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_8x5 : return ( ( w + 7 ) / 8 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_8x6 : return ( ( w + 7 ) / 8 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_8x8 : return ( ( w + 7 ) / 8 ) * ( ( h + 7 ) / 8 ) * d * 16 ;
case FORMAT_ASTC_10x5 : return ( ( w + 9 ) / 10 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_10x6 : return ( ( w + 9 ) / 10 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_10x8 : return ( ( w + 9 ) / 10 ) * ( ( h + 7 ) / 8 ) * d * 16 ;
case FORMAT_ASTC_10x10 : return ( ( w + 9 ) / 10 ) * ( ( h + 9 ) / 10 ) * d * 16 ;
case FORMAT_ASTC_12x10 : return ( ( w + 11 ) / 12 ) * ( ( h + 9 ) / 10 ) * d * 16 ;
case FORMAT_ASTC_12x12 : return ( ( w + 11 ) / 12 ) * ( ( h + 11 ) / 12 ) * d * 16 ;
default : lovrUnreachable ( ) ;
}
}
2022-05-26 06:52:24 +00:00
// Errors if a 3D texture region exceeds the texture's bounds
static void checkTextureBounds ( const TextureInfo * info , uint32_t offset [ 4 ] , uint32_t extent [ 3 ] ) {
uint32_t maxWidth = MAX ( info - > width > > offset [ 3 ] , 1 ) ;
uint32_t maxHeight = MAX ( info - > height > > offset [ 3 ] , 1 ) ;
2022-07-30 22:08:30 +00:00
uint32_t maxLayers = info - > type = = TEXTURE_3D ? MAX ( info - > layers > > offset [ 3 ] , 1 ) : info - > layers ;
2022-05-26 06:52:24 +00:00
lovrCheck ( offset [ 0 ] + extent [ 0 ] < = maxWidth , " Texture x range [%d,%d] exceeds width (%d) " , offset [ 0 ] , offset [ 0 ] + extent [ 0 ] , maxWidth ) ;
lovrCheck ( offset [ 1 ] + extent [ 1 ] < = maxHeight , " Texture y range [%d,%d] exceeds height (%d) " , offset [ 1 ] , offset [ 1 ] + extent [ 1 ] , maxHeight ) ;
2022-07-30 22:08:30 +00:00
lovrCheck ( offset [ 2 ] + extent [ 2 ] < = maxLayers , " Texture layer range [%d,%d] exceeds layer count (%d) " , offset [ 2 ] , offset [ 2 ] + extent [ 2 ] , maxLayers ) ;
2022-05-26 06:52:24 +00:00
lovrCheck ( offset [ 3 ] < info - > mipmaps , " Texture mipmap %d exceeds its mipmap count (%d) " , offset [ 3 ] + 1 , info - > mipmaps ) ;
}
2022-06-23 02:05:36 +00:00
static void mipmapTexture ( gpu_stream * stream , Texture * texture , uint32_t base , uint32_t count ) {
if ( count = = ~ 0u ) count = texture - > info . mipmaps - ( base + 1 ) ;
bool volumetric = texture - > info . type = = TEXTURE_3D ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
uint32_t level = base + i + 1 ;
uint32_t srcOffset [ 4 ] = { 0 , 0 , 0 , level - 1 } ;
uint32_t dstOffset [ 4 ] = { 0 , 0 , 0 , level } ;
uint32_t srcExtent [ 3 ] = {
MAX ( texture - > info . width > > ( level - 1 ) , 1 ) ,
MAX ( texture - > info . height > > ( level - 1 ) , 1 ) ,
2022-07-30 22:08:30 +00:00
volumetric ? MAX ( texture - > info . layers > > ( level - 1 ) , 1 ) : 1
2022-06-23 02:05:36 +00:00
} ;
uint32_t dstExtent [ 3 ] = {
MAX ( texture - > info . width > > level , 1 ) ,
MAX ( texture - > info . height > > level , 1 ) ,
2022-07-30 22:08:30 +00:00
volumetric ? MAX ( texture - > info . layers > > level , 1 ) : 1
2022-06-23 02:05:36 +00:00
} ;
gpu_blit ( stream , texture - > gpu , texture - > gpu , srcOffset , dstOffset , srcExtent , dstExtent , GPU_FILTER_LINEAR ) ;
gpu_sync ( stream , & ( gpu_barrier ) {
. prev = GPU_PHASE_TRANSFER ,
. next = GPU_PHASE_TRANSFER ,
. flush = GPU_CACHE_TRANSFER_WRITE ,
. clear = GPU_CACHE_TRANSFER_READ
} , 1 ) ;
}
}
2022-06-04 21:28:23 +00:00
static ShaderResource * findShaderResource ( Shader * shader , const char * name , size_t length , uint32_t slot ) {
if ( name ) {
uint32_t hash = ( uint32_t ) hash64 ( name , length ) ;
for ( uint32_t i = 0 ; i < shader - > resourceCount ; i + + ) {
if ( shader - > resources [ i ] . hash = = hash ) {
return & shader - > resources [ i ] ;
}
2022-05-24 06:10:11 +00:00
}
2022-06-04 21:28:23 +00:00
lovrThrow ( " Shader has no variable named '%s' " , name ) ;
} else {
for ( uint32_t i = 0 ; i < shader - > resourceCount ; i + + ) {
if ( shader - > resources [ i ] . binding = = slot ) {
return & shader - > resources [ i ] ;
}
}
lovrThrow ( " Shader has no variable in slot '%d' " , slot ) ;
2022-05-24 06:10:11 +00:00
}
2022-06-04 21:28:23 +00:00
}
static void trackBuffer ( Pass * pass , Buffer * buffer , gpu_phase phase , gpu_cache cache ) {
2022-06-04 21:54:04 +00:00
if ( lovrBufferIsTemporary ( buffer ) ) {
return ; // Scratch buffers are write-only from CPU and read-only from GPU, no sync needed
}
2022-06-04 21:28:23 +00:00
Access access = {
. buffer = buffer ,
. sync = & buffer - > sync ,
. phase = phase ,
. cache = cache
} ;
arr_push ( & pass - > access , access ) ;
lovrRetain ( buffer ) ;
}
static void trackTexture ( Pass * pass , Texture * texture , gpu_phase phase , gpu_cache cache ) {
2022-08-06 07:50:25 +00:00
if ( ! texture | | texture = = state . window ) {
2022-06-12 05:55:43 +00:00
return ;
}
2022-06-12 02:07:46 +00:00
if ( texture - > info . parent ) {
texture = texture - > info . parent ;
}
2022-06-04 21:54:04 +00:00
if ( texture - > info . usage = = TEXTURE_SAMPLE ) {
return ; // If the texture is sample-only, no sync needed (initial upload is handled manually)
}
2022-06-04 21:28:23 +00:00
Access access = {
. texture = texture ,
. sync = & texture - > sync ,
. phase = phase ,
. cache = cache
} ;
arr_push ( & pass - > access , access ) ;
lovrRetain ( texture ) ;
2022-05-24 06:10:11 +00:00
}
2022-08-06 07:50:25 +00:00
static void trackMaterial ( Pass * pass , Material * material , gpu_phase phase , gpu_cache cache ) {
if ( ! material - > hasWritableTexture ) {
return ;
}
trackTexture ( pass , material - > info . texture , phase , cache ) ;
trackTexture ( pass , material - > info . glowTexture , phase , cache ) ;
trackTexture ( pass , material - > info . occlusionTexture , phase , cache ) ;
trackTexture ( pass , material - > info . metalnessTexture , phase , cache ) ;
trackTexture ( pass , material - > info . roughnessTexture , phase , cache ) ;
trackTexture ( pass , material - > info . clearcoatTexture , phase , cache ) ;
trackTexture ( pass , material - > info . normalTexture , phase , cache ) ;
}
2022-07-04 00:26:31 +00:00
static void updateModelTransforms ( Model * model , uint32_t nodeIndex , float * parent ) {
mat4 global = model - > globalTransforms + 16 * nodeIndex ;
NodeTransform * local = & model - > localTransforms [ nodeIndex ] ;
vec3 T = local - > properties [ PROP_TRANSLATION ] ;
quat R = local - > properties [ PROP_ROTATION ] ;
vec3 S = local - > properties [ PROP_SCALE ] ;
mat4_init ( global , parent ) ;
mat4_translate ( global , T [ 0 ] , T [ 1 ] , T [ 2 ] ) ;
mat4_rotateQuat ( global , R ) ;
mat4_scale ( global , S [ 0 ] , S [ 1 ] , S [ 2 ] ) ;
ModelNode * node = & model - > info . data - > nodes [ nodeIndex ] ;
for ( uint32_t i = 0 ; i < node - > childCount ; i + + ) {
updateModelTransforms ( model , node - > children [ i ] , global ) ;
}
}
2022-05-22 22:09:09 +00:00
// Only an explicit set of SPIR-V capabilities are allowed
// Some capabilities require a GPU feature to be supported
// Some common unsupported capabilities are checked directly, to provide better error messages
static void checkShaderFeatures ( uint32_t * features , uint32_t count ) {
for ( uint32_t i = 0 ; i < count ; i + + ) {
switch ( features [ i ] ) {
case 0 : break ; // Matrix
case 1 : break ; // Shader
case 2 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " geometry shading " ) ;
case 3 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " tessellation shading " ) ;
case 5 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " linkage " ) ;
case 9 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " half floats " ) ;
case 10 : lovrCheck ( state . features . float64 , " GPU does not support shader feature #%d: %s " , features [ i ] , " 64 bit floats " ) ; break ;
case 11 : lovrCheck ( state . features . int64 , " GPU does not support shader feature #%d: %s " , features [ i ] , " 64 bit integers " ) ; break ;
case 12 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " 64 bit atomics " ) ;
case 22 : lovrCheck ( state . features . int16 , " GPU does not support shader feature #%d: %s " , features [ i ] , " 16 bit integers " ) ; break ;
case 23 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " tessellation shading " ) ;
case 24 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " geometry shading " ) ;
case 25 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " extended image gather " ) ;
case 27 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " multisample storage textures " ) ;
case 32 : lovrCheck ( state . limits . clipDistances > 0 , " GPU does not support shader feature #%d: %s " , features [ i ] , " clip distance " ) ; break ;
case 33 : lovrCheck ( state . limits . cullDistances > 0 , " GPU does not support shader feature #%d: %s " , features [ i ] , " cull distance " ) ; break ;
case 34 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " cubemap array textures " ) ;
case 35 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " sample rate shading " ) ;
case 36 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " rectangle textures " ) ;
case 37 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " rectangle textures " ) ;
case 39 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " 8 bit integers " ) ;
case 40 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " input attachments " ) ;
case 41 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " sparse residency " ) ;
case 42 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " min LOD " ) ;
case 43 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " 1D textures " ) ;
case 44 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " 1D textures " ) ;
case 45 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " cubemap array textures " ) ;
case 46 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " texel buffers " ) ;
case 47 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " texel buffers " ) ;
case 48 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " multisampled storage textures " ) ;
case 49 : break ; // StorageImageExtendedFormats (?)
case 50 : break ; // ImageQuery
case 51 : break ; // DerivativeControl
case 52 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " sample rate shading " ) ;
case 53 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " transform feedback " ) ;
case 54 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " geometry shading " ) ;
case 55 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " autoformat storage textures " ) ;
case 56 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " autoformat storage textures " ) ;
case 57 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " multiviewport " ) ;
case 69 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " layered rendering " ) ;
case 70 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " multiviewport " ) ;
case 4427 : break ; // ShaderDrawParameters
case 4437 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " multigpu " ) ;
case 4439 : lovrCheck ( state . limits . renderSize [ 2 ] > 1 , " GPU does not support shader feature #%d: %s " , features [ i ] , " multiview " ) ; break ;
case 5301 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " non-uniform indexing " ) ;
case 5306 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " non-uniform indexing " ) ;
case 5307 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " non-uniform indexing " ) ;
case 5308 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " non-uniform indexing " ) ;
case 5309 : lovrThrow ( " Shader uses unsupported feature #%d: %s " , features [ i ] , " non-uniform indexing " ) ;
default : lovrThrow ( " Shader uses unknown feature #%d " , features [ i ] ) ;
}
}
}
2022-04-21 07:27:13 +00:00
static void onMessage ( void * context , const char * message , bool severe ) {
if ( severe ) {
2022-06-09 06:59:36 +00:00
lovrThrow ( " GPU error: %s " , message ) ;
2022-04-21 07:27:13 +00:00
} else {
lovrLog ( LOG_DEBUG , " GPU " , message ) ;
}
}