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-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-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-06-17 06:49:09 +00:00
# define MATERIALS_PER_BLOCK 1024
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 ;
typedef struct {
struct { float x , y ; } position ;
2022-06-21 01:26:15 +00:00
struct { float u , v ; } uv ;
2022-06-19 00:43:12 +00:00
} GlyphVertex ;
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 ;
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-05-22 22:10:07 +00:00
uint32_t layout ;
uint32_t computePipeline ;
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-06-19 00:43:12 +00:00
typedef struct {
uint32_t codepoint ;
uint16_t atlas [ 4 ] ;
float advance ;
2022-06-21 01:26:15 +00:00
float box [ 4 ] ;
float uv [ 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-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 19:29:00 +00:00
typedef struct {
float view [ 16 ] ;
float projection [ 16 ] ;
float viewProjection [ 16 ] ;
float inverseProjection [ 16 ] ;
} Camera ;
2022-05-11 19:50:26 +00:00
typedef struct {
float color [ 4 ] ;
Shader * shader ;
2022-06-08 03:42:10 +00:00
Sampler * sampler ;
2022-06-17 06:49:09 +00:00
Material * material ;
2022-05-30 22:36:31 +00:00
uint64_t formatHash ;
2022-05-11 19:50:26 +00:00
gpu_pipeline_info info ;
2022-06-06 19:38:15 +00:00
float viewport [ 4 ] ;
float depthRange [ 2 ] ;
uint32_t scissor [ 4 ] ;
2022-06-17 06:49:09 +00:00
VertexMode drawMode ;
2022-05-11 19:50:26 +00:00
bool dirty ;
} Pipeline ;
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-06-25 02:59:48 +00:00
VERTEX_EMPTY ,
2022-06-17 06:49:09 +00:00
VERTEX_FORMAT_COUNT
} VertexFormat ;
2022-06-12 02:07:46 +00:00
typedef struct {
2022-06-17 06:49:09 +00:00
VertexMode 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
const void * data ;
void * * pointer ;
uint32_t count ;
} vertex ;
struct {
Buffer * buffer ;
const void * data ;
void * * pointer ;
uint32_t count ;
uint32_t stride ;
} index ;
uint32_t start ;
uint32_t count ;
uint32_t instances ;
uint32_t base ;
} Draw ;
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 ;
PassInfo info ;
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 ;
gpu_binding builtins [ 3 ] ;
gpu_buffer * vertexBuffer ;
gpu_buffer * indexBuffer ;
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 ;
typedef struct {
gpu_texture * texture ;
uint32_t hash ;
uint32_t tick ;
} TempAttachment ;
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 ;
uint32_t cursor ;
uint32_t length ;
} 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-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-05-11 22:38:01 +00:00
float background [ 4 ] ;
2022-05-11 22:28:04 +00:00
Texture * window ;
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-05-28 03:47:07 +00:00
Shader * defaultShaders [ DEFAULT_SHADER_COUNT ] ;
2022-06-17 06:49:09 +00:00
gpu_vertex_format vertexFormats [ VERTEX_FORMAT_COUNT ] ;
Material * defaultMaterial ;
2022-06-18 00:43:26 +00:00
uint32_t materialBlock ;
2022-06-17 06:49:09 +00:00
arr_t ( MaterialBlock ) materialBlocks ;
2022-06-10 03:05:31 +00:00
arr_t ( TempAttachment ) attachments ;
2022-06-18 00:43:26 +00:00
arr_t ( Pass * ) passes ;
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 ;
uint32_t builtinLayout ;
2022-06-17 06:49:09 +00:00
uint32_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-06-04 21:28:23 +00:00
static void * tempGrow ( void * p , size_t size ) ;
2022-04-29 05:30:31 +00:00
static void beginFrame ( void ) ;
2022-06-21 01:26:15 +00:00
static void cleanupPasses ( void ) ;
2022-05-24 04:40:57 +00:00
static uint32_t getLayout ( gpu_slot * slots , uint32_t count ) ;
2022-05-30 19:17:17 +00:00
static gpu_bundle * getBundle ( uint32_t layout ) ;
2022-05-11 22:28:04 +00:00
static gpu_texture * getAttachment ( uint32_t size [ 2 ] , uint32_t layers , TextureFormat format , bool srgb , uint32_t samples ) ;
2022-04-30 03:38:34 +00:00
static size_t measureTexture ( TextureFormat format , uint16_t w , uint16_t h , uint16_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-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-05-11 22:28:04 +00:00
bool lovrGraphicsInit ( bool debug , bool vsync ) {
2022-04-19 02:30:58 +00:00
if ( state . initialized ) return false ;
2022-04-21 07:27:13 +00:00
gpu_config config = {
. debug = 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
if ( os_window_is_open ( ) ) {
config . vk . getInstanceExtensions = os_vk_get_instance_extensions ;
config . vk . createSurface = os_vk_create_surface ;
config . vk . surface = true ;
config . vk . vsync = vsync ;
}
# endif
2022-06-06 03:38:14 +00:00
# if defined LOVR_VK && !defined LOVR_DISABLE_HEADSET
if ( lovrHeadsetInterface ) {
config . vk . getPhysicalDevice = lovrHeadsetInterface - > getVulkanPhysicalDevice ;
config . vk . createInstance = lovrHeadsetInterface - > createVulkanInstance ;
config . vk . createDevice = lovrHeadsetInterface - > createVulkanDevice ;
}
# endif
2022-04-21 07:27:13 +00:00
if ( ! gpu_init ( & config ) ) {
lovrThrow ( " Failed to initialize GPU " ) ;
}
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-06-10 03:05:31 +00:00
arr_init ( & state . attachments , realloc ) ;
2022-06-18 00:43:26 +00:00
arr_init ( & state . passes , realloc ) ;
2022-05-24 04:40:57 +00:00
2022-06-17 06:49:09 +00:00
gpu_slot builtinSlots [ ] = {
2022-05-30 19:29:00 +00:00
{ 0 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_ALL } , // Cameras
{ 1 , GPU_SLOT_UNIFORM_BUFFER , GPU_STAGE_ALL } , // Draw data
{ 2 , 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 ,
. depth = 1 ,
. 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-21 01:26:15 +00:00
. attributes [ 2 ] = { 0 , 12 , offsetof ( GlyphVertex , uv ) , GPU_TYPE_F32x2 } ,
. attributes [ 3 ] = { 1 , 13 , 16 , GPU_TYPE_F32x4 } ,
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-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-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-06-21 01:26:15 +00:00
cleanupPasses ( ) ;
2022-06-01 04:45:54 +00:00
lovrRelease ( state . window , lovrTextureDestroy ) ;
2022-06-10 03:05:31 +00:00
for ( uint32_t i = 0 ; i < state . attachments . length ; i + + ) {
gpu_texture_destroy ( state . attachments . data [ i ] . texture ) ;
free ( state . attachments . data [ i ] . texture ) ;
2022-05-11 19:51:13 +00:00
}
2022-06-10 03:05:31 +00:00
arr_free ( & state . attachments ) ;
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-06-01 04:45:54 +00:00
for ( uint32_t i = 0 ; i < COUNTOF ( state . defaultShaders ) ; i + + ) {
lovrRelease ( state . defaultShaders [ i ] , lovrShaderDestroy ) ;
}
2022-06-17 06:49:09 +00:00
lovrRelease ( state . defaultMaterial , lovrMaterialDestroy ) ;
for ( uint32_t i = 0 ; i < state . materialBlocks . length ; i + + ) {
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-05-24 04:44:42 +00:00
for ( uint32_t i = 0 ; i < state . pipelines . length ; i + + ) {
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-05-24 04:40:57 +00:00
for ( uint32_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-06-04 21:28:23 +00:00
limits - > uniformBuffersPerStage = MIN ( state . limits . uniformBuffersPerStage - 2 , MAX_SHADER_RESOURCES ) ;
limits - > storageBuffersPerStage = MIN ( state . limits . storageBuffersPerStage , MAX_SHADER_RESOURCES ) ;
limits - > sampledTexturesPerStage = MIN ( state . limits . sampledTexturesPerStage , MAX_SHADER_RESOURCES ) ;
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-04-21 09:16:17 +00:00
memcpy ( limits - > computeDispatchCount , state . limits . computeDispatchCount , 3 * sizeof ( uint32_t ) ) ;
memcpy ( limits - > computeWorkgroupSize , state . limits . computeWorkgroupSize , 3 * sizeof ( uint32_t ) ) ;
limits - > computeWorkgroupVolume = state . limits . computeWorkgroupVolume ;
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-05-11 22:38:01 +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-06-21 01:58:12 +00:00
Font * lovrGraphicsGetDefaultFont ( ) {
if ( ! state . defaultFont ) {
state . defaultFont = lovrFontCreate ( & ( FontInfo ) {
2022-06-21 22:28:03 +00:00
. rasterizer = lovrRasterizerCreate ( NULL , 32 ) ,
2022-06-21 01:58:12 +00:00
. padding = 1 ,
. spread = 4.
} ) ;
}
return state . defaultFont ;
}
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-06-12 05:55:43 +00:00
// End passes
for ( uint32_t i = 0 ; i < count ; i + + ) {
streams [ i + 1 ] = passes [ i ] - > stream ;
2022-06-12 02:07:46 +00:00
2022-05-11 19:51:13 +00:00
if ( passes [ i ] - > info . type = = PASS_RENDER ) {
gpu_render_end ( passes [ i ] - > stream ) ;
2022-06-23 02:05:36 +00:00
if ( ! passes [ i ] - > info . canvas . mipmap ) {
continue ;
}
Canvas * canvas = & passes [ i ] - > info . canvas ;
for ( uint32_t j = 0 ; j < 4 & & canvas - > textures [ j ] ; j + + ) {
if ( canvas - > textures [ i ] - > 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 ( passes [ i ] - > stream , canvas - > textures [ i ] , 0 , ~ 0u ) ;
}
}
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 ( passes [ i ] - > stream , canvas - > depth . texture , 0 , ~ 0u ) ;
}
2022-06-04 18:54:05 +00:00
} else if ( passes [ i ] - > info . type = = PASS_COMPUTE ) {
gpu_compute_end ( passes [ i ] - > stream ) ;
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 ] ;
// Only the first write in a pass is considered, because each pass can only perform one type
// of write to a resource, and there is not currently any intra-pass synchronization.
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
}
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 ;
}
}
for ( uint32_t i = 0 ; i < count ; i + + ) {
gpu_sync ( streams [ i ] , & barriers [ i ] , 1 ) ;
}
for ( uint32_t i = 0 ; i < total ; i + + ) {
2022-04-29 05:30:31 +00:00
gpu_stream_end ( streams [ i ] ) ;
}
2022-06-12 05:55:43 +00:00
gpu_submit ( streams , total ) ;
2022-06-12 02:07:46 +00:00
2022-06-21 01:26:15 +00:00
cleanupPasses ( ) ;
2022-06-18 00:43:26 +00:00
2022-06-12 02:07:46 +00:00
if ( state . window ) {
state . window - > gpu = NULL ;
state . window - > renderView = NULL ;
}
2022-04-29 05:30:31 +00:00
2022-06-12 05:55:43 +00:00
state . stream = NULL ;
2022-04-29 05:30:31 +00:00
state . active = false ;
}
2022-04-29 05:37:03 +00:00
void lovrGraphicsWait ( ) {
gpu_wait ( ) ;
}
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-04-26 22:32:54 +00:00
Buffer * lovrBufferCreate ( 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 ) {
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 ,
. depth = 1 ,
. mipmaps = 1 ,
. samples = 1 ,
. usage = TEXTURE_RENDER ,
. srgb = true
} ;
}
if ( ! state . window - > gpu ) {
beginFrame ( ) ;
state . window - > gpu = gpu_surface_acquire ( ) ;
state . window - > renderView = state . window - > gpu ;
}
return state . window ;
}
2022-04-30 03:38:34 +00:00
Texture * lovrTextureCreate ( TextureInfo * info ) {
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-05-01 01:49:46 +00:00
uint32_t mipmapCap = log2 ( MAX ( MAX ( info - > width , info - > height ) , ( info - > type = = TEXTURE_3D ? info - > depth : 1 ) ) ) + 1 ;
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 " ) ;
lovrCheck ( info - > depth > 0 , " Texture depth must be greater than zero " ) ;
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-04-30 03:56:23 +00:00
lovrCheck ( info - > depth < = limit | | info - > type ! = TEXTURE_3D , " Texture %s exceeds the limit for this texture type (%d) " , " depth " , limit ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > depth < = state . limits . textureLayers | | info - > type ! = TEXTURE_ARRAY , " Texture %s exceeds the limit for this texture type (%d) " , " depth " , limit ) ;
lovrCheck ( info - > depth = = 1 | | info - > type ! = TEXTURE_2D , " 2D textures must have a depth of 1 " ) ;
lovrCheck ( info - > depth = = 6 | | info - > type ! = TEXTURE_CUBE , " Cubemaps must have a depth of 6 " ) ;
lovrCheck ( info - > width = = info - > height | | info - > type ! = TEXTURE_CUBE , " Cubemaps must be square " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( measureTexture ( info - > format , info - > width , info - > height , info - > depth ) < 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 ) ;
levelSizes [ level ] = measureTexture ( info - > format , width , height , info - > depth ) ;
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 + + ) {
for ( uint32_t layer = 0 ; layer < info - > depth ; layer + + ) {
Image * image = info - > imageCount = = 1 ? info - > images [ 0 ] : info - > images [ layer ] ;
uint32_t slice = info - > imageCount = = 1 ? layer : 0 ;
uint32_t size = lovrImageGetLayerSize ( image , level ) ;
lovrCheck ( size = = levelSizes [ level ] , " 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 ,
. size = { info - > width , info - > height , info - > depth } ,
. mipmaps = texture - > info . mipmaps ,
. samples = MAX ( info - > samples , 1 ) ,
. usage =
( ( info - > usage & TEXTURE_SAMPLE ) ? GPU_TEXTURE_SAMPLE : 0 ) |
( ( info - > usage & TEXTURE_RENDER ) ? GPU_TEXTURE_RENDER : 0 ) |
( ( info - > usage & TEXTURE_STORAGE ) ? GPU_TEXTURE_STORAGE : 0 ) |
2022-06-12 02:07:46 +00:00
( ( info - > usage & TEXTURE_TRANSFER ) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0 ) |
( ( info - > usage = = TEXTURE_RENDER ) ? GPU_TEXTURE_TRANSIENT : 0 ) ,
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-04-30 16:13:21 +00:00
if ( ( info - > usage & TEXTURE_RENDER ) & & info - > type ! = TEXTURE_3D & & info - > depth < = 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 ,
. layerCount = info - > depth ,
. 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-04-30 03:56:23 +00:00
Texture * lovrTextureCreateView ( TextureViewInfo * view ) {
const TextureInfo * info = & view - > parent - > info ;
uint32_t maxDepth = info - > type = = TEXTURE_3D ? MAX ( info - > depth > > view - > levelIndex , 1 ) : info - > depth ;
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 " ) ;
lovrCheck ( view - > layerIndex + view - > layerCount < = maxDepth , " Texture view layer range exceeds depth of parent texture " ) ;
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 ) ;
texture - > info . depth = view - > layerCount ;
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-05-01 22:47:17 +00:00
Sampler * lovrSamplerCreate ( SamplerInfo * info ) {
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
Blob * lovrGraphicsCompileShader ( ShaderStage stage , Blob * source ) {
2022-05-22 21:58:07 +00:00
uint32_t spirv = 0x07230203 ;
if ( source - > size % 4 = = 0 & & source - > size > = 4 & & ! memcmp ( source - > data , & spirv , 4 ) ) {
return lovrRetain ( source ) , source ;
}
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-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 " ;
const char * suffixes [ ] = {
[ STAGE_VERTEX ] = " void main() { FragColor = Color * VertexColor, FragUV = VertexUV, Position = lovrmain(); } " ,
[ STAGE_FRAGMENT ] = " void main() { PixelColors[0] = lovrmain(); } " ,
[ STAGE_COMPUTE ] = " void main() { lovrmain(); } "
} ;
2022-05-09 18:47:06 +00:00
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 ,
source - > data ,
suffixes [ stage ]
2022-06-10 00:44:20 +00:00
} ;
int lengths [ ] = {
2022-06-16 03:46:43 +00:00
- 1 ,
etc_shaders_lovr_glsl_len ,
source - > size ,
- 1
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 ) ;
if ( ! glslang_shader_preprocess ( shader , & input ) ) {
lovrLog ( LOG_INFO , " Could not preprocess shader: %s " , glslang_shader_get_info_log ( shader ) ) ;
return NULL ;
}
if ( ! glslang_shader_parse ( shader , & input ) ) {
lovrLog ( LOG_INFO , " Could not parse shader: %s " , glslang_shader_get_info_log ( shader ) ) ;
return NULL ;
}
glslang_program_t * program = glslang_program_create ( ) ;
glslang_program_add_shader ( program , shader ) ;
if ( ! glslang_program_link ( program , 0 ) ) {
lovrLog ( LOG_INFO , " Could not link shader: %s " , glslang_program_get_info_log ( program ) ) ;
return NULL ;
}
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 ;
void * data = malloc ( size ) ;
lovrAssert ( data , " Out of memory " ) ;
memcpy ( data , words , size ) ;
Blob * blob = lovrBlobCreate ( data , size , " SPIRV " ) ;
glslang_program_delete ( program ) ;
glslang_shader_delete ( shader ) ;
return blob ;
2022-05-22 22:09:09 +00:00
# else
2022-05-09 18:47:06 +00:00
return NULL ;
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 ) ;
shader - > computePipeline = state . pipelines . length ;
arr_push ( & state . pipelines , pipeline ) ;
}
2022-05-09 18:47:06 +00:00
}
2022-05-28 03:47:07 +00:00
Shader * lovrGraphicsGetDefaultShader ( DefaultShader type ) {
if ( state . defaultShaders [ type ] ) {
return state . defaultShaders [ type ] ;
}
ShaderInfo info = { . type = SHADER_GRAPHICS } ;
switch ( type ) {
case SHADER_UNLIT :
info . stages [ 0 ] = lovrBlobCreate ( ( void * ) lovr_shader_unlit_vert , sizeof ( lovr_shader_unlit_vert ) , " Unlit Vertex Shader " ) ;
info . stages [ 1 ] = lovrBlobCreate ( ( void * ) lovr_shader_unlit_frag , sizeof ( lovr_shader_unlit_frag ) , " Unlit Fragment Shader " ) ;
info . label = " unlit " ;
break ;
2022-06-21 01:26:15 +00:00
case SHADER_FONT :
info . stages [ 0 ] = lovrBlobCreate ( ( void * ) lovr_shader_unlit_vert , sizeof ( lovr_shader_unlit_vert ) , " Unlit Vertex Shader " ) ;
info . stages [ 1 ] = lovrBlobCreate ( ( void * ) lovr_shader_font_frag , sizeof ( lovr_shader_font_frag ) , " Font Fragment Shader " ) ;
info . label = " font " ;
break ;
2022-06-25 02:59:48 +00:00
case SHADER_FILL :
info . stages [ 0 ] = lovrBlobCreate ( ( void * ) lovr_shader_fill_vert , sizeof ( lovr_shader_fill_vert ) , " Fill Vertex Shader " ) ;
info . stages [ 1 ] = lovrBlobCreate ( ( void * ) lovr_shader_unlit_frag , sizeof ( lovr_shader_unlit_frag ) , " Unlit Fragment Shader " ) ;
info . label = " fill " ;
break ;
2022-05-28 03:47:07 +00:00
default : lovrUnreachable ( ) ;
}
state . defaultShaders [ type ] = lovrShaderCreate ( & info ) ;
2022-05-30 22:36:31 +00:00
info . stages [ 0 ] - > data = NULL ;
info . stages [ 1 ] - > data = NULL ;
2022-05-28 03:47:07 +00:00
lovrRelease ( info . stages [ 0 ] , lovrBlobDestroy ) ;
lovrRelease ( info . stages [ 1 ] , lovrBlobDestroy ) ;
return state . defaultShaders [ type ] ;
}
2022-05-09 18:47:06 +00:00
Shader * lovrShaderCreate ( ShaderInfo * info ) {
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 + + ) {
result = spv_parse ( info - > stages [ i ] - > data , info - > stages [ i ] - > size , & spv [ i ] ) ;
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 ) ) ;
result = spv_parse ( info - > stages [ i ] - > data , info - > stages [ i ] - > size , & spv [ i ] ) ;
lovrCheck ( result = = SPV_OK , " Failed to load Shader: %s \n " , spv_result_to_string ( result ) ) ;
checkShaderFeatures ( spv [ i ] . features , spv [ i ] . featureCount ) ;
}
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 + + ;
shader - > flagLookup [ index ] = ( uint32_t ) hash64 ( constant - > name , strlen ( constant - > name ) ) ;
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 = {
. stages [ 0 ] = { info - > stages [ 0 ] - > data , info - > stages [ 0 ] - > size } ,
2022-05-22 22:09:09 +00:00
. stages [ 1 ] = { info - > stages [ 1 ] - > data , info - > stages [ 1 ] - > size } ,
. pushConstantSize = shader - > constantSize ,
. label = info - > label
2022-05-09 18:47:06 +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-05-09 18:47:06 +00:00
free ( shader ) ;
}
const ShaderInfo * lovrShaderGetInfo ( Shader * shader ) {
return & shader - > info ;
}
2022-06-17 06:49:09 +00:00
// Material
Material * lovrMaterialCreate ( 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
if ( ! block | | block - > head = = ~ 0u | | ! gpu_finished ( block - > list [ block - > head ] . tick ) ) {
bool found = false ;
for ( size_t i = 0 ; i < state . materialBlocks . length ; i + + ) {
block = & state . materialBlocks . data [ i ] ;
if ( block - > head ! = ~ 0u & & gpu_finished ( 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-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-06-18 00:43:26 +00:00
block - > list [ i ] . block = 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 ;
lovrCheck ( texture - > info . type = = TEXTURE_2D , " Material textures must be 2D " ) ;
bindings [ i + 1 ] = ( gpu_binding ) { i + 1 , GPU_SLOT_SAMPLED_TEXTURE , . texture = texture - > gpu } ;
}
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
Font * lovrFontCreate ( FontInfo * info ) {
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 ) ;
// 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 ) ;
uint32_t width = ( uint32_t ) ceilf ( box [ 2 ] - box [ 0 ] ) ;
uint32_t height = ( uint32_t ) ceilf ( box [ 3 ] - box [ 1 ] ) ;
while ( font - > atlasWidth < 2 * width | | font - > atlasHeight < 2 * height ) {
font - > atlasWidth < < = 1 ;
font - > atlasHeight < < = 1 ;
}
2022-06-26 02:54:13 +00:00
font - > pixelDensity = lovrRasterizerGetHeight ( info - > rasterizer ) ;
font - > lineSpacing = 1.f ;
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-25 22:26:42 +00:00
static Glyph * lovrFontGetGlyph ( Font * font , uint32_t codepoint ) {
uint64_t hash = hash64 ( & codepoint , 4 ) ;
uint64_t index = map_get ( & font - > glyphLookup , hash ) ;
if ( index ! = MAP_NIL ) {
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 ;
glyph - > advance = lovrRasterizerGetGlyphAdvance ( font - > info . rasterizer , codepoint ) ;
if ( lovrRasterizerIsGlyphEmpty ( font - > info . rasterizer , codepoint ) ) {
memset ( glyph - > atlas , 0 , sizeof ( glyph - > atlas ) ) ;
memset ( glyph - > box , 0 , sizeof ( glyph - > atlas ) ) ;
memset ( glyph - > uv , 0 , sizeof ( glyph - > atlas ) ) ;
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 ] ;
uint32_t pixelWidth = 2 * font - > info . padding + ( uint32_t ) ceilf ( width ) ;
uint32_t pixelHeight = 2 * font - > info . padding + ( uint32_t ) ceilf ( height ) ;
if ( font - > atlasX + pixelWidth > font - > atlasWidth ) {
font - > atlasX = font - > atlasWidth = = font - > atlasHeight ? 0 : font - > atlasWidth > > 1 ;
font - > atlasY + = font - > rowHeight ;
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 ;
}
}
}
glyph - > atlas [ 0 ] = font - > atlasX ;
glyph - > atlas [ 1 ] = font - > atlasY ;
glyph - > atlas [ 2 ] = pixelWidth ;
glyph - > atlas [ 3 ] = pixelHeight ;
double unused ;
// The glyph is centered in its rasterized rectangle (needs to be kept in sync with Rasterizer)
glyph - > uv [ 0 ] = font - > atlasX + font - > info . padding + ( 1.f - modf ( width , & unused ) ) / 2.f ;
glyph - > uv [ 1 ] = font - > atlasY + font - > info . padding + ( 1.f - modf ( height , & unused ) ) / 2.f ;
glyph - > uv [ 2 ] = glyph - > uv [ 0 ] + width ;
glyph - > uv [ 3 ] = glyph - > uv [ 1 ] + height ;
font - > atlasX + = pixelWidth ;
font - > rowHeight = MAX ( font - > rowHeight , font - > atlasY + pixelHeight ) ;
return glyph ;
}
static void lovrFontUploadNewGlyphs ( Font * font , uint32_t start ) {
if ( start > = font - > glyphs . length ) {
return ;
}
if ( ! font - > atlas | | font - > atlasWidth > font - > atlas - > info . width | | font - > atlasHeight > font - > atlas - > info . height ) {
lovrCheck ( font - > atlasWidth < = 65536 , " Font atlas is too big! " ) ;
Texture * atlas = lovrTextureCreate ( & ( TextureInfo ) {
. type = TEXTURE_2D ,
. format = FORMAT_RGBA32F ,
. width = font - > atlasWidth ,
. height = font - > atlasHeight ,
. depth = 1 ,
. 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 ;
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
} ) ;
}
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
for ( uint32_t i = start ; i < font - > glyphs . length ; i + + ) {
Glyph * glyph = & font - > glyphs . data [ i ] ;
uint32_t width = glyph - > atlas [ 2 ] ;
uint32_t height = glyph - > atlas [ 3 ] ;
if ( width = = 0 | | height = = 0 ) {
continue ;
}
void * pixels = gpu_map ( scratchpad , width * height * 16 , 16 , GPU_MAP_WRITE ) ;
lovrRasterizerGetGlyphPixels ( font - > info . rasterizer , glyph - > codepoint , pixels , width , height , font - > info . spread , font - > info . padding ) ;
uint32_t dstOffset [ 4 ] = { glyph - > atlas [ 0 ] , glyph - > atlas [ 1 ] , 0 , 0 } ;
uint32_t extent [ 3 ] = { width , height , 1 } ;
gpu_copy_buffer_texture ( state . stream , scratchpad , font - > atlas - > gpu , 0 , dstOffset , extent ) ;
}
state . hasGlyphUpload = true ;
}
static float lovrFontGetKerning ( Font * font , uint32_t previous , uint32_t codepoint ) {
uint32_t codepoints [ ] = { previous , codepoint } ;
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 ) {
kerning . f32 = lovrRasterizerGetKerning ( font - > info . rasterizer , previous , codepoint ) ;
map_set ( & font - > kerning , hash , kerning . u64 ) ;
}
return kerning . f32 ;
}
2022-04-29 05:30:31 +00:00
// Pass
Pass * lovrGraphicsGetPass ( PassInfo * info ) {
beginFrame ( ) ;
Pass * pass = tempAlloc ( sizeof ( Pass ) ) ;
pass - > ref = 1 ;
pass - > info = * info ;
pass - > stream = gpu_stream_begin ( info - > label ) ;
2022-06-04 21:28:23 +00:00
arr_init ( & pass - > access , tempGrow ) ;
2022-06-18 00:43:26 +00:00
arr_push ( & state . passes , pass ) ;
lovrRetain ( pass ) ;
2022-05-11 19:51:13 +00:00
2022-06-04 18:54:05 +00:00
if ( info - > type = = PASS_TRANSFER ) {
return pass ;
}
if ( info - > type = = PASS_COMPUTE ) {
memset ( pass - > constants , 0 , sizeof ( pass - > constants ) ) ;
pass - > constantsDirty = true ;
pass - > bindingMask = 0 ;
pass - > bindingsDirty = true ;
pass - > pipeline = & pass - > pipelines [ 0 ] ;
pass - > pipeline - > shader = NULL ;
pass - > pipeline - > dirty = true ;
gpu_compute_begin ( pass - > stream ) ;
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 ;
const TextureInfo * main = canvas - > textures [ 0 ] ? & canvas - > textures [ 0 ] - > info : & canvas - > depth . texture - > info ;
lovrCheck ( canvas - > textures [ 0 ] | | canvas - > depth . texture , " Render pass must have at least one color or depth texture " ) ;
lovrCheck ( main - > width < = state . limits . renderSize [ 0 ] , " Render pass width (%d) exceeds the renderSize limit of this GPU (%d) " , main - > width , state . limits . renderSize [ 0 ] ) ;
lovrCheck ( main - > height < = state . limits . renderSize [ 1 ] , " Render pass height (%d) exceeds the renderSize limit of this GPU (%d) " , main - > height , state . limits . renderSize [ 1 ] ) ;
lovrCheck ( main - > depth < = state . limits . renderSize [ 2 ] , " Render pass view count (%d) exceeds the renderSize limit of this GPU (%d) " , main - > depth , state . limits . renderSize [ 2 ] ) ;
lovrCheck ( canvas - > samples = = 1 | | canvas - > samples = = 4 , " Render pass sample count must be 1 or 4...for now " ) ;
uint32_t colorTextureCount = 0 ;
for ( uint32_t i = 0 ; i < COUNTOF ( canvas - > textures ) & & canvas - > textures [ i ] ; i + + , colorTextureCount + + ) {
const TextureInfo * texture = & canvas - > textures [ i ] - > info ;
bool renderable = texture - > format = = GPU_FORMAT_SURFACE | | ( state . features . formats [ texture - > format ] & GPU_FEATURE_RENDER ) ;
lovrCheck ( renderable , " This GPU does not support rendering to the texture format used by Canvas texture #%d " , i + 1 ) ;
lovrCheck ( texture - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( texture - > width = = main - > width , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > height = = main - > height , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > depth = = main - > depth , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > samples = = main - > samples , " Render pass texture sample counts must match " ) ;
}
if ( canvas - > depth . texture | | canvas - > depth . format ) {
TextureFormat format = canvas - > depth . texture ? canvas - > depth . texture - > info . format : canvas - > depth . format ;
bool renderable = state . features . formats [ format ] & GPU_FEATURE_RENDER ;
lovrCheck ( format = = FORMAT_D16 | | format = = FORMAT_D24S8 | | format = = FORMAT_D32F , " Depth buffer must use a depth format " ) ;
lovrCheck ( renderable , " This GPU does not support depth buffers with this texture format " ) ;
if ( canvas - > depth . texture ) {
const TextureInfo * texture = & canvas - > depth . texture - > info ;
2022-05-11 19:51:13 +00:00
lovrCheck ( texture - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( texture - > width = = main - > width , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > height = = main - > height , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > depth = = main - > depth , " Render pass texture sizes must match " ) ;
2022-05-30 19:29:00 +00:00
lovrCheck ( texture - > samples = = main - > samples , " Depth buffer sample count must match the main render pass sample count...for now " ) ;
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-06-04 16:47:02 +00:00
// Render target
2022-05-30 19:29:00 +00:00
gpu_canvas target = {
. size = { main - > width , main - > height }
} ;
for ( uint32_t i = 0 ; i < colorTextureCount ; i + + ) {
if ( main - > samples = = 1 & & canvas - > samples > 1 ) {
2022-06-04 21:28:23 +00:00
lovrCheck ( canvas - > loads [ i ] ! = LOAD_KEEP , " When internal multisampling is used, render pass textures must be cleared " ) ;
2022-05-30 19:29:00 +00:00
TextureFormat format = canvas - > textures [ i ] - > info . format ;
bool srgb = canvas - > textures [ i ] - > info . srgb ;
target . color [ i ] . texture = getAttachment ( target . size , main - > depth , format , srgb , canvas - > samples ) ;
target . color [ i ] . resolve = canvas - > textures [ i ] - > renderView ;
} else {
target . color [ i ] . texture = canvas - > textures [ i ] - > renderView ;
2022-05-11 19:51:13 +00:00
}
2022-05-30 19:29:00 +00:00
target . color [ i ] . load = ( gpu_load_op ) canvas - > loads [ i ] ;
target . color [ i ] . save = GPU_SAVE_OP_SAVE ;
target . color [ i ] . clear [ 0 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 0 ] ) ;
target . color [ i ] . clear [ 1 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 1 ] ) ;
target . color [ i ] . clear [ 2 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 2 ] ) ;
target . color [ i ] . clear [ 3 ] = canvas - > clears [ i ] [ 2 ] ;
2022-06-04 21:28:23 +00:00
2022-06-23 02:05:36 +00:00
if ( info - > canvas . mipmap & & canvas - > textures [ i ] - > info . mipmaps > 1 ) {
trackTexture ( pass , canvas - > textures [ 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 , canvas - > textures [ i ] , GPU_PHASE_COLOR , cache ) ;
}
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 ) {
target . depth . texture = canvas - > depth . texture - > renderView ;
2022-06-23 02:05:36 +00:00
if ( info - > canvas . mipmap & & canvas - > depth . texture - > info . mipmaps > 1 ) {
trackTexture ( pass , canvas - > depth . texture , 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 , canvas - > depth . texture , phase , cache ) ;
}
2022-06-04 08:33:11 +00:00
} else if ( canvas - > depth . format ) {
2022-05-30 19:29:00 +00:00
target . depth . texture = getAttachment ( target . size , main - > depth , canvas - > depth . format , false , canvas - > samples ) ;
}
2022-05-11 19:51:13 +00:00
2022-06-04 08:33:11 +00:00
if ( target . depth . texture ) {
target . depth . load = target . depth . stencilLoad = ( gpu_load_op ) canvas - > depth . load ;
target . depth . save = canvas - > depth . texture ? GPU_SAVE_OP_SAVE : GPU_SAVE_OP_DISCARD ;
target . depth . clear . depth = canvas - > depth . clear ;
}
2022-05-11 19:51:13 +00:00
2022-06-04 16:47:02 +00:00
// Begin render pass
2022-05-11 19:51:13 +00:00
2022-06-04 16:47:02 +00:00
gpu_render_begin ( pass - > stream , & target ) ;
2022-06-01 04:20:01 +00:00
2022-06-19 06:31:51 +00:00
// The default Buffer (filled with zeros/ones) is always at slot #1, used for default vertex data
gpu_bind_vertex_buffers ( pass - > stream , & state . defaultBuffer - > gpu , NULL , 1 , 1 ) ;
2022-06-04 16:47:02 +00:00
// Reset state
2022-05-24 06:10:11 +00:00
pass - > transform = pass - > transforms [ 0 ] ;
2022-05-30 19:29:00 +00:00
mat4_identity ( pass - > transform ) ;
2022-05-24 06:10:11 +00:00
pass - > pipeline = & pass - > pipelines [ 0 ] ;
2022-06-04 16:47:02 +00:00
pass - > pipeline - > info = ( gpu_pipeline_info ) {
. colorCount = colorTextureCount ,
. depth . format = canvas - > depth . texture ? canvas - > depth . texture - > info . format : canvas - > depth . format ,
. multisample . count = canvas - > samples ,
. viewCount = main - > depth ,
. depth . test = GPU_COMPARE_LEQUAL ,
. depth . write = true
} ;
2022-05-30 22:36:31 +00:00
for ( uint32_t i = 0 ; i < colorTextureCount ; i + + ) {
pass - > pipeline - > info . color [ i ] . format = canvas - > textures [ i ] - > info . format ;
pass - > pipeline - > info . color [ i ] . srgb = canvas - > textures [ i ] - > info . srgb ;
2022-06-04 16:47:02 +00:00
pass - > pipeline - > info . color [ i ] . mask = 0xf ;
2022-05-30 22:36:31 +00:00
}
2022-06-04 16:47:02 +00:00
float defaultColor [ 4 ] = { 1.f , 1.f , 1.f , 1.f } ;
memcpy ( pass - > pipeline - > color , defaultColor , sizeof ( defaultColor ) ) ;
pass - > pipeline - > formatHash = 0 ;
2022-05-30 19:29:00 +00:00
pass - > pipeline - > shader = NULL ;
2022-06-17 06:49:09 +00:00
pass - > pipeline - > drawMode = VERTEX_TRIANGLES ;
2022-05-30 19:29:00 +00:00
pass - > pipeline - > dirty = true ;
2022-06-18 00:43:26 +00:00
pass - > pipeline - > material = state . defaultMaterial ;
lovrRetain ( pass - > pipeline - > material ) ;
2022-06-17 06:49:09 +00:00
pass - > materialDirty = 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 ;
pass - > cameraCount = main - > depth ;
pass - > cameras = tempAlloc ( pass - > cameraCount * sizeof ( Camera ) ) ;
2022-05-30 22:36:31 +00:00
for ( uint32_t i = 0 ; i < pass - > cameraCount ; i + + ) {
mat4_identity ( pass - > cameras [ i ] . view ) ;
2022-06-04 16:47:02 +00:00
mat4_perspective ( pass - > cameras [ i ] . projection , .01f , 100.f , 1.0f , ( float ) main - > width / main - > height ) ;
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-05-30 22:36:31 +00:00
pass - > drawCount = 0 ;
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 ) } ;
pass - > builtins [ 0 ] = ( gpu_binding ) { 0 , GPU_SLOT_UNIFORM_BUFFER , . buffer = cameras } ;
pass - > builtins [ 1 ] = ( gpu_binding ) { 1 , GPU_SLOT_UNIFORM_BUFFER , . buffer = draws } ;
2022-06-08 03:42:10 +00:00
pass - > builtins [ 2 ] = ( gpu_binding ) { 2 , GPU_SLOT_SAMPLER , . sampler = NULL } ;
pass - > pipeline - > sampler = state . defaultSamplers [ FILTER_LINEAR ] ;
pass - > samplerDirty = true ;
2022-06-18 00:43:26 +00:00
lovrRetain ( pass - > pipeline - > sampler ) ;
2022-05-30 22:36:31 +00:00
pass - > vertexBuffer = NULL ;
pass - > indexBuffer = NULL ;
2022-06-06 19:38:15 +00:00
float viewport [ 6 ] = { 0.f , 0.f , ( float ) main - > width , ( float ) main - > height , 0.f , 1.f } ;
lovrPassSetViewport ( pass , viewport , viewport + 4 ) ;
uint32_t scissor [ 4 ] = { 0 , 0 , main - > width , main - > height } ;
lovrPassSetScissor ( pass , scissor ) ;
2022-04-29 05:30:31 +00:00
return pass ;
}
void lovrPassDestroy ( void * ref ) {
2022-06-04 21:28:23 +00:00
//
2022-04-29 05:30:31 +00:00
}
const PassInfo * lovrPassGetInfo ( Pass * pass ) {
return & pass - > info ;
}
2022-05-30 19:29:00 +00:00
void lovrPassGetViewMatrix ( Pass * pass , uint32_t index , float * viewMatrix ) {
lovrCheck ( index < pass - > cameraCount , " Invalid camera index '%d' " , index ) ;
mat4_init ( viewMatrix , pass - > cameras [ index ] . view ) ;
}
void lovrPassSetViewMatrix ( Pass * pass , uint32_t index , float * viewMatrix ) {
lovrCheck ( index < pass - > cameraCount , " Invalid camera index '%d' " , index ) ;
mat4_init ( pass - > cameras [ index ] . view , viewMatrix ) ;
pass - > cameraDirty = true ;
}
void lovrPassGetProjection ( Pass * pass , uint32_t index , float * projection ) {
lovrCheck ( index < pass - > cameraCount , " Invalid camera index '%d' " , index ) ;
mat4_init ( projection , pass - > cameras [ index ] . projection ) ;
}
void lovrPassSetProjection ( Pass * pass , uint32_t index , float * projection ) {
lovrCheck ( index < pass - > cameraCount , " Invalid camera index '%d' " , index ) ;
mat4_init ( pass - > cameras [ index ] . projection , projection ) ;
pass - > cameraDirty = true ;
}
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 ;
case STACK_PIPELINE :
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-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 ;
case STACK_PIPELINE :
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-06-06 19:38:15 +00:00
gpu_set_viewport ( pass - > stream , pass - > pipeline - > viewport , pass - > pipeline - > depthRange ) ;
gpu_set_scissor ( pass - > stream , 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-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-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 ] ) {
gpu_set_scissor ( pass - > stream , scissor ) ;
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
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 ) {
pass - > bindingMask & = ~ ( 1 < < shader - > resources [ j ] . binding ) ;
}
i + + ;
j + + ;
}
}
}
uint32_t shaderSlots = ( shader - > bufferMask | shader - > textureMask | shader - > samplerMask ) ;
uint32_t missingResources = shaderSlots & ~ pass - > bindingMask ;
// 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 ) ;
if ( ~ missingResources & bit ) {
continue ;
}
pass - > bindings [ i ] . number = i ;
pass - > bindings [ i ] . type = shader - > resources [ i ] . type ;
if ( shader - > bufferMask & bit ) {
2022-06-19 06:31:51 +00:00
pass - > bindings [ i ] . buffer . object = state . defaultBuffer - > gpu ;
pass - > bindings [ i ] . buffer . offset = 0 ;
pass - > bindings [ i ] . buffer . extent = state . defaultBuffer - > size ;
2022-05-24 05:32:36 +00:00
} else if ( shader - > textureMask & bit ) {
pass - > bindings [ i ] . texture = state . defaultTexture - > gpu ;
} else if ( shader - > samplerMask & bit ) {
2022-06-08 03:42:10 +00:00
pass - > bindings [ i ] . sampler = state . defaultSamplers [ FILTER_LINEAR ] - > gpu ;
2022-05-24 05:32:36 +00:00
}
pass - > bindingMask | = bit ;
}
pass - > bindingsDirty = true ;
}
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 ;
2022-05-24 05:32:36 +00:00
pass - > pipeline - > info . shader = shader - > gpu ;
pass - > pipeline - > info . flags = shader - > flags ;
pass - > pipeline - > info . flagCount = shader - > overrideCount ;
2022-05-11 19:50:26 +00:00
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-17 06:49:09 +00:00
void lovrPassSetVertexMode ( Pass * pass , VertexMode mode ) {
pass - > pipeline - > drawMode = mode ;
}
2022-06-06 19:38:15 +00:00
void lovrPassSetViewport ( Pass * pass , float viewport [ 4 ] , float depthRange [ 2 ] ) {
gpu_set_viewport ( pass - > stream , viewport , depthRange ) ;
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 ) {
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
lovrCheck ( shader - > bufferMask & ( 1 < < slot ) , " Trying to send a Buffer to slot %d, but the active Shader doesn't have a Buffer in that slot " ) ;
lovrCheck ( offset < buffer - > size , " Buffer offset is past the end of the Buffer " ) ;
uint32_t limit ;
if ( shader - > storageMask & ( 1 < < 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 ;
pass - > bindingMask | = ( 1 < < slot ) ;
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 ;
cache = ( shader - > storageMask & ( 1 < < slot ) ) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_UNIFORM ;
} else {
phase = GPU_PHASE_SHADER_COMPUTE ;
cache = ( shader - > storageMask & ( 1 < < slot ) ) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_UNIFORM ; // TODO readonly
}
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
lovrCheck ( shader - > textureMask & ( 1 < < slot ) , " Trying to send a Texture to slot %d, but the active Shader doesn't have a Texture in that slot " ) ;
if ( shader - > storageMask & ( 1 < < 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 ;
pass - > bindingMask | = ( 1 < < slot ) ;
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 ;
cache = ( shader - > storageMask & ( 1 < < slot ) ) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_TEXTURE ;
} else {
phase = GPU_PHASE_SHADER_COMPUTE ;
cache = ( shader - > storageMask & ( 1 < < slot ) ) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_TEXTURE ; // TODO readonly
}
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
lovrCheck ( shader - > samplerMask & ( 1 < < slot ) , " Trying to send a Sampler to slot %d, but the active Shader doesn't have a Sampler in that slot " ) ;
pass - > bindings [ slot ] . sampler = sampler - > gpu ;
pass - > bindingMask | = ( 1 < < slot ) ;
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 ;
}
}
lovrThrow ( " Shader has no push constant named '%s' " , name ) ;
}
2022-05-30 22:36:31 +00:00
static void flushPipeline ( Pass * pass , Draw * draw , Shader * shader ) {
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-06-16 03:46:43 +00:00
// Builtin vertex format
if ( ! draw - > vertex . buffer & & pipeline - > formatHash ! = 1 + draw - > vertex . format ) {
pipeline - > formatHash = 1 + draw - > vertex . format ;
pipeline - > info . vertex = state . vertexFormats [ draw - > vertex . format ] ;
pipeline - > dirty = true ;
2022-05-30 22:36:31 +00:00
2022-06-16 03:46:43 +00:00
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 ) {
2022-06-19 06:31:51 +00:00
. buffer = 1 ,
2022-06-16 03:46:43 +00:00
. location = shader - > attributes [ i ] . location ,
. type = GPU_TYPE_F32x4 ,
2022-06-19 06:31:51 +00:00
. offset = shader - > attributes [ i ] . location = = LOCATION_COLOR ? 16 : 0
2022-05-30 22:36:31 +00:00
} ;
}
}
}
2022-06-16 03:46:43 +00:00
}
2022-05-30 22:36:31 +00:00
2022-06-16 03:46:43 +00:00
// Custom vertex format
if ( draw - > vertex . buffer & & pipeline - > formatHash ! = draw - > vertex . buffer - > hash ) {
pipeline - > formatHash = draw - > vertex . buffer - > hash ;
pipeline - > info . vertex . bufferCount = 2 ;
pipeline - > info . vertex . attributeCount = shader - > attributeCount ;
2022-06-19 06:31:51 +00:00
pipeline - > info . vertex . bufferStrides [ 0 ] = draw - > vertex . buffer - > info . stride ;
pipeline - > info . vertex . bufferStrides [ 1 ] = 0 ;
2022-05-30 22:36:31 +00:00
pipeline - > dirty = true ;
2022-06-16 03:46:43 +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 ] ;
if ( field . hash ? ( field . hash = = attribute - > hash ) : ( field . location = = attribute - > location ) ) {
pipeline - > info . vertex . attributes [ i ] = ( gpu_attribute ) {
2022-06-19 06:31:51 +00:00
. buffer = 0 ,
2022-06-16 03:46:43 +00:00
. location = attribute - > location ,
. offset = field . offset ,
. type = field . type
} ;
found = true ;
break ;
}
}
if ( ! found ) {
pipeline - > info . vertex . attributes [ i ] = ( gpu_attribute ) {
2022-06-19 06:31:51 +00:00
. buffer = 1 ,
2022-06-16 03:46:43 +00:00
. location = attribute - > location ,
2022-06-19 06:31:51 +00:00
. offset = attribute - > location = = LOCATION_COLOR ? 16 : 0 ,
2022-06-16 03:46:43 +00:00
. type = GPU_TYPE_F32x4
} ;
}
}
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 ;
}
static void flushConstants ( Pass * pass , Shader * shader ) {
if ( pass - > constantsDirty & & shader - > constantSize > 0 ) {
gpu_push_constants ( pass - > stream , shader - > gpu , pass - > constants , shader - > constantSize ) ;
pass - > constantsDirty = false ;
}
}
static void flushBindings ( Pass * pass , Shader * shader ) {
if ( ! pass - > bindingsDirty | | shader - > resourceCount = = 0 ) {
return ;
}
2022-06-04 18:54:05 +00:00
uint32_t set = pass - > info . type = = PASS_RENDER ? 2 : 0 ;
2022-05-30 22:36:31 +00:00
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
} ;
gpu_bundle * bundle = getBundle ( shader - > layout ) ;
gpu_bundle_write ( & bundle , & info , 1 ) ;
2022-06-04 18:54:05 +00:00
gpu_bind_bundle ( pass - > stream , shader - > gpu , set , bundle , NULL , 0 ) ;
2022-05-30 22:36:31 +00:00
}
static void flushBuiltins ( Pass * pass , Draw * draw , Shader * shader ) {
bool rebind = 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 ) ;
}
uint32_t size = pass - > cameraCount * sizeof ( Camera ) ;
void * data = gpu_map ( pass - > builtins [ 0 ] . buffer . object , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
memcpy ( data , pass - > cameras , size ) ;
pass - > cameraDirty = false ;
rebind = true ;
}
if ( pass - > drawCount % 256 = = 0 ) {
uint32_t size = 256 * sizeof ( DrawData ) ;
pass - > drawData = gpu_map ( pass - > builtins [ 1 ] . buffer . object , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
rebind = true ;
}
2022-06-08 03:42:10 +00:00
if ( pass - > samplerDirty ) {
pass - > builtins [ 2 ] . sampler = pass - > pipeline - > sampler - > gpu ;
pass - > samplerDirty = false ;
rebind = true ;
}
2022-05-30 22:36:31 +00:00
if ( rebind ) {
gpu_bundle_info bundleInfo = {
. layout = state . layouts . data [ state . builtinLayout ] . gpu ,
. bindings = pass - > builtins ,
. count = COUNTOF ( pass - > builtins )
} ;
gpu_bundle * bundle = getBundle ( state . builtinLayout ) ;
gpu_bundle_write ( & bundle , & bundleInfo , 1 ) ;
gpu_bind_bundle ( pass - > stream , shader - > gpu , 0 , bundle , NULL , 0 ) ;
}
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 ) ;
mat4_cofactor ( cofactor ) ;
memcpy ( pass - > drawData - > transform , transform , 64 ) ;
memcpy ( pass - > drawData - > cofactor , cofactor , 64 ) ;
memcpy ( pass - > drawData - > color , pass - > pipeline - > color , 16 ) ;
pass - > drawData + + ;
}
2022-06-17 06:49:09 +00:00
static void flushMaterial ( Pass * pass , Draw * draw , Shader * shader ) {
if ( draw - > material & & draw - > material ! = pass - > pipeline - > material ) {
gpu_bind_bundle ( pass - > stream , shader - > gpu , 1 , draw - > material - > bundle , NULL , 0 ) ;
pass - > materialDirty = true ;
} else if ( pass - > materialDirty ) {
gpu_bind_bundle ( pass - > stream , shader - > gpu , 1 , pass - > pipeline - > material - > bundle , NULL , 0 ) ;
pass - > materialDirty = false ;
}
}
2022-05-30 22:36:31 +00:00
static void flushBuffers ( Pass * pass , Draw * draw ) {
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 ( ) ) ;
void * pointer = gpu_map ( scratchpad , size , stride , GPU_MAP_WRITE ) ;
if ( draw - > vertex . pointer ) {
* draw - > vertex . pointer = pointer ;
} else {
memcpy ( pointer , draw - > vertex . data , size ) ;
}
2022-06-19 06:31:51 +00:00
gpu_bind_vertex_buffers ( pass - > stream , & scratchpad , NULL , 0 , 1 ) ;
2022-05-30 22:36:31 +00:00
pass - > vertexBuffer = NULL ;
} 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 ) {
uint32_t stride = draw - > index . stride ? draw - > index . stride : sizeof ( uint16_t ) ;
uint32_t size = draw - > index . count * stride ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
void * pointer = gpu_map ( scratchpad , size , stride , GPU_MAP_WRITE ) ;
if ( draw - > index . pointer ) {
* draw - > index . pointer = pointer ;
} else {
memcpy ( pointer , draw - > index . data , size ) ;
}
gpu_index_type type = stride = = 4 ? GPU_INDEX_U32 : GPU_INDEX_U16 ;
gpu_bind_index_buffer ( pass - > stream , scratchpad , 0 , type ) ;
pass - > indexBuffer = NULL ;
} 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
}
}
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 ) ;
flushPipeline ( pass , draw , shader ) ;
flushConstants ( pass , shader ) ;
flushBindings ( pass , shader ) ;
flushBuiltins ( pass , draw , shader ) ;
2022-06-17 06:49:09 +00:00
flushMaterial ( pass , draw , shader ) ;
2022-05-30 22:36:31 +00:00
flushBuffers ( pass , draw ) ;
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-06-17 06:49:09 +00:00
. mode = VERTEX_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-06-17 06:49:09 +00:00
. mode = VERTEX_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-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 ) {
. mode = VERTEX_LINES ,
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
} else {
indexCount = ( cols * rows ) * 6 ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. 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-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 ;
uint16_t cell [ ] = { a , b , c , c , b , d } ;
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 ) {
if ( style = = STYLE_LINE ) {
static ShapeVertex vertices [ ] = {
{ { - .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-06-22 07:05:26 +00:00
static uint16_t indices [ ] = {
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 ) {
. mode = VERTEX_LINES ,
. transform = transform ,
. vertex . data = vertices ,
. vertex . count = COUNTOF ( vertices ) ,
. index . data = indices ,
. index . count = COUNTOF ( indices )
} ) ;
} else {
ShapeVertex vertices [ ] = {
{ { - .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-06-22 07:05:26 +00:00
uint16_t indices [ ] = {
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 ) {
. mode = VERTEX_TRIANGLES ,
. transform = transform ,
. vertex . data = vertices ,
. vertex . count = COUNTOF ( vertices ) ,
. index . data = indices ,
. index . count = COUNTOF ( indices )
} ) ;
}
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-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 ) {
. mode = VERTEX_LINES ,
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
} else {
uint32_t vertexCount = segments + 2 ;
uint32_t indexCount = segments * 3 ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. 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-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 ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
// 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 ;
}
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 ) {
. mode = VERTEX_TRIANGLES ,
. transform = transform ,
. vertex . pointer = ( void * * ) & vertices ,
. vertex . count = vertexCount ,
. index . pointer = ( void * * ) & indices ,
. index . count = indexCount
} ) ;
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 ;
uint16_t quad [ ] = { a , b , c , c , b , d } ;
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
if ( capped ) {
// Cap centers
* vertices + + = ( ShapeVertex ) { { 0.f , 0.f , - .5f } , { 0.f , 0.f , - .5f } , { .5f , .5f } } ;
* vertices + + = ( ShapeVertex ) { { 0.f , 0.f , .5f } , { 0.f , 0.f , .5f } , { .5f , .5f } } ;
// Caps
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 } , { 0.f , 0.f , - .5f } , { x + .5f , y - .5f } } ;
* vertices + + = ( ShapeVertex ) { { x , y , .5f } , { 0.f , 0.f , .5f } , { x + .5f , y - .5f } } ;
}
// 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 ;
uint16_t wedge1 [ ] = { a + 0 , b + 0 , c + 0 } ;
uint16_t wedge2 [ ] = { a + 1 , b + 1 , c + 1 } ;
memcpy ( indices + 0 , wedge1 , sizeof ( wedge1 ) ) ;
memcpy ( indices + 3 , wedge2 , sizeof ( wedge2 ) ) ;
indices + = 6 ;
}
}
}
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 ;
uint32_t vertexCount = segmentsT * segmentsP ;
uint32_t indexCount = segmentsT * segmentsP * 6 ;
ShapeVertex * vertices ;
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. 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 ;
uint16_t quad [ ] = { a , b , c , b , c , d } ;
memcpy ( indices , quad , sizeof ( quad ) ) ;
indices + = COUNTOF ( quad ) ;
}
}
}
2022-06-25 22:26:42 +00:00
static void aline ( GlyphVertex * vertices , uint32_t head , uint32_t tail , HorizontalAlign align ) {
if ( align = = ALIGN_LEFT ) return ;
float shift = align / 2.f * vertices [ tail - 1 ] . position . x ;
for ( uint32_t i = head ; i < tail ; i + + ) {
vertices [ i ] . position . x - = shift ;
2022-06-21 01:26:15 +00:00
}
2022-06-25 22:26:42 +00:00
}
2022-06-21 01:26:15 +00:00
2022-06-25 22:26:42 +00:00
void lovrPassText ( Pass * pass , Font * font , const char * text , uint32_t length , float * transform , float wrap , HorizontalAlign halign , VerticalAlign valign ) {
font = font ? font : lovrGraphicsGetDefaultFont ( ) ;
uint32_t originalGlyphCount = font - > glyphs . length ;
2022-06-21 01:26:15 +00:00
GlyphVertex * vertices = tempAlloc ( length * 4 * sizeof ( GlyphVertex ) ) ;
uint32_t vertexCount = 0 ;
uint32_t glyphCount = 0 ;
uint32_t lineCount = 1 ;
uint32_t lineStart = 0 ;
uint32_t wordStart = 0 ;
float x = 0.f ;
float y = 0.f ;
2022-06-26 02:54:13 +00:00
float leading = lovrRasterizerGetLeading ( font - > info . rasterizer ) * font - > lineSpacing ;
float height = lovrRasterizerGetHeight ( font - > info . rasterizer ) ;
float ascent = lovrRasterizerGetAscent ( font - > info . rasterizer ) ;
float scale = 1.f / font - > pixelDensity ;
2022-06-21 01:26:15 +00:00
wrap / = scale ;
size_t bytes ;
uint32_t codepoint ;
uint32_t previous = ' \0 ' ;
const char * end = text + length ;
while ( ( bytes = utf8_decode ( text , end , & codepoint ) ) > 0 ) {
2022-06-25 22:26:42 +00:00
if ( codepoint = = ' ' | | codepoint = = ' \t ' ) {
Glyph * glyph = lovrFontGetGlyph ( font , ' ' ) ;
2022-06-21 01:52:10 +00:00
wordStart = vertexCount ;
2022-06-25 22:26:42 +00:00
x + = codepoint = = ' \t ' ? glyph - > advance * 4.f : glyph - > advance ;
2022-06-21 01:52:10 +00:00
previous = ' \0 ' ;
text + = bytes ;
continue ;
2022-06-21 01:26:15 +00:00
} else if ( codepoint = = ' \n ' ) {
2022-06-25 22:26:42 +00:00
aline ( vertices , lineStart , vertexCount , halign ) ;
2022-06-21 01:26:15 +00:00
lineStart = vertexCount ;
wordStart = vertexCount ;
2022-06-25 22:26:42 +00:00
x = 0.f ;
2022-06-21 01:26:15 +00:00
y - = leading ;
lineCount + + ;
2022-06-25 22:26:42 +00:00
previous = ' \0 ' ;
text + = bytes ;
2022-06-21 01:26:15 +00:00
continue ;
}
2022-06-25 22:26:42 +00:00
Glyph * glyph = lovrFontGetGlyph ( font , codepoint ) ;
2022-06-21 01:26:15 +00:00
2022-06-25 22:26:42 +00:00
// Keming
if ( previous ) x + = lovrFontGetKerning ( font , previous , codepoint ) ;
2022-06-21 01:26:15 +00:00
previous = codepoint ;
// Wrap
if ( wrap > 0.f & & x + glyph - > box [ 2 ] > wrap & & wordStart ! = lineStart ) {
float dx = wordStart = = vertexCount ? x : vertices [ wordStart ] . position . x ;
float dy = leading ;
// Shift the vertices of the overflowing word down a line and back to the beginning
for ( uint32_t i = wordStart ; i < vertexCount ; i + + ) {
vertices [ i ] . position . x - = dx ;
vertices [ i ] . position . y - = dy ;
}
2022-06-25 22:26:42 +00:00
aline ( vertices , lineStart , wordStart , halign ) ;
2022-06-21 01:26:15 +00:00
lineStart = wordStart ;
lineCount + + ;
x - = dx ;
y - = dy ;
}
2022-06-25 22:26:42 +00:00
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
if ( vertexCount = = lineStart ) {
x - = glyph - > box [ 0 ] ;
2022-06-21 01:26:15 +00:00
}
2022-06-25 22:26:42 +00:00
// Vertices
float * bb = glyph - > box ;
float * uv = glyph - > uv ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , y + bb [ 3 ] } , { uv [ 0 ] , uv [ 1 ] } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , y + bb [ 3 ] } , { uv [ 2 ] , uv [ 1 ] } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 0 ] , y + bb [ 1 ] } , { uv [ 0 ] , uv [ 3 ] } } ;
vertices [ vertexCount + + ] = ( GlyphVertex ) { { x + bb [ 2 ] , y + bb [ 1 ] } , { uv [ 2 ] , uv [ 3 ] } } ;
glyphCount + + ;
2022-06-21 01:26:15 +00:00
// Advance
x + = glyph - > advance ;
text + = bytes ;
}
2022-06-25 22:26:42 +00:00
// Align last line
aline ( vertices , lineStart , vertexCount , halign ) ;
2022-06-21 01:26:15 +00:00
2022-06-25 22:26:42 +00:00
// Normalize UVs now that final atlas size is known
for ( uint32_t i = 0 ; i < vertexCount ; i + + ) {
2022-06-21 01:26:15 +00:00
vertices [ i ] . uv . u / = font - > atlasWidth ;
vertices [ i ] . uv . v / = font - > atlasHeight ;
}
2022-06-25 22:26:42 +00:00
// Resize atlas, rasterize new glyphs, upload them into atlas, recreate material
lovrFontUploadNewGlyphs ( font , originalGlyphCount ) ;
2022-06-21 01:26:15 +00:00
mat4_scale ( transform , scale , scale , scale ) ;
2022-06-26 02:54:13 +00:00
float totalHeight = height + leading * ( lineCount - 1 ) ;
2022-06-21 01:26:15 +00:00
mat4_translate ( transform , 0.f , - ascent + valign / 2.f * totalHeight , 0.f ) ;
uint16_t * indices ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. shader = SHADER_FONT ,
. material = font - > material ,
. transform = transform ,
. vertex . format = VERTEX_GLYPH ,
. vertex . count = vertexCount ,
. vertex . data = vertices ,
. index . count = glyphCount * 6 ,
. index . pointer = ( void * * ) & indices
} ) ;
for ( uint32_t i = 0 ; i < vertexCount ; i + = 4 ) {
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-25 02:59:48 +00:00
void lovrPassFill ( Pass * pass , Texture * texture ) {
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. shader = SHADER_FILL ,
. 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 ) {
uint32_t vertexCount = COUNTOF ( monkey_vertices ) / 6 ;
ShapeVertex * vertices ;
lovrPassDraw ( pass , & ( Draw ) {
. mode = VERTEX_TRIANGLES ,
. vertex . count = vertexCount ,
. vertex . pointer = ( void * * ) & vertices ,
. index . count = COUNTOF ( monkey_indices ) ,
. index . data = monkey_indices ,
. transform = transform
} ) ;
// 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-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 ) {
count = ( indices ? indices : vertices ) - > info . length - start ;
}
if ( indices ) {
lovrCheck ( count < = indices - > info . length - start , " Mesh draw range exceeds index buffer size " ) ;
} else {
lovrCheck ( count < = vertices - > info . length - start , " Mesh draw range exceeds vertex buffer size " ) ;
}
lovrPassDraw ( pass , & ( Draw ) {
. mode = pass - > pipeline - > drawMode ,
. vertex . buffer = vertices ,
. index . buffer = indices ,
. transform = transform ,
. start = start ,
. count = count ,
. instances = instances
} ) ;
}
2022-06-10 06:38:33 +00:00
void lovrPassMultimesh ( Pass * pass , Buffer * vertices , Buffer * indices , Buffer * draws , uint32_t count , uint32_t offset , uint32_t stride ) {
lovrCheck ( pass - > info . type = = PASS_RENDER , " This function can only be called on a render pass " ) ;
lovrCheck ( offset % 4 = = 0 , " Multimesh draw buffer offset must be a multiple of 4 " ) ;
uint32_t commandSize = indices ? 20 : 16 ;
stride = stride ? stride : commandSize ;
uint32_t totalSize = stride * ( count - 1 ) + commandSize ;
lovrCheck ( offset + totalSize < draws - > size , " Multimesh draw range exceeds size of draw buffer " ) ;
Draw draw = ( Draw ) {
. mode = pass - > pipeline - > drawMode ,
. vertex . buffer = vertices ,
. index . buffer = indices
} ;
Shader * shader = pass - > pipeline - > shader ;
lovrCheck ( shader , " A custom Shader must be bound to draw a multimesh " ) ;
flushPipeline ( pass , & draw , shader ) ;
flushConstants ( pass , shader ) ;
flushBindings ( pass , shader ) ;
flushBuiltins ( pass , & draw , shader ) ;
flushBuffers ( pass , & draw ) ;
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 " ) ;
lovrCheck ( x < = state . limits . computeDispatchCount [ 0 ] , " Compute %s count exceeds computeDispatchCount limit " , " x " ) ;
lovrCheck ( y < = state . limits . computeDispatchCount [ 1 ] , " Compute %s count exceeds computeDispatchCount limit " , " y " ) ;
lovrCheck ( z < = state . limits . computeDispatchCount [ 2 ] , " Compute %s count exceeds computeDispatchCount limit " , " z " ) ;
gpu_pipeline * pipeline = state . pipelines . data [ shader - > computePipeline ] ;
if ( pass - > pipeline - > dirty ) {
gpu_bind_pipeline ( pass - > stream , pipeline , true ) ;
pass - > pipeline - > dirty = false ;
}
flushConstants ( pass , shader ) ;
flushBindings ( pass , shader ) ;
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-05-26 02:45:01 +00:00
lovrCheck ( texture - > info . type = = TEXTURE_3D | | layer + layerCount < = texture - > info . depth , " Texture clear range exceeds texture layer count " ) ;
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
}
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 ] ) ;
if ( extent [ 2 ] = = ~ 0u ) extent [ 2 ] = MIN ( texture - > info . depth - dstOffset [ 2 ] , lovrImageGetLayerCount ( image ) - srcOffset [ 2 ] ) ;
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 ) ;
size_t rowSize = measureTexture ( texture - > info . format , extent [ 0 ] , 1 , 1 ) ;
size_t totalSize = measureTexture ( texture - > info . format , extent [ 0 ] , extent [ 1 ] , 1 ) * extent [ 2 ] ;
size_t layerOffset = measureTexture ( texture - > info . format , extent [ 0 ] , srcOffset [ 1 ] , 1 ) ;
layerOffset + = measureTexture ( texture - > info . format , srcOffset [ 0 ] , 1 , 1 ) ;
size_t pitch = measureTexture ( texture - > info . format , lovrImageGetWidth ( image , srcOffset [ 3 ] ) , 1 , 1 ) ;
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 ] ) ;
if ( extent [ 2 ] = = ~ 0u ) extent [ 2 ] = MIN ( src - > info . depth - srcOffset [ 2 ] , dst - > info . depth - dstOffset [ 0 ] ) ;
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 ] ;
if ( srcExtent [ 2 ] = = ~ 0u ) srcExtent [ 2 ] = src - > info . depth - srcOffset [ 2 ] ;
if ( dstExtent [ 0 ] = = ~ 0u ) dstExtent [ 0 ] = dst - > info . width - dstOffset [ 0 ] ;
if ( dstExtent [ 1 ] = = ~ 0u ) dstExtent [ 1 ] = dst - > info . height - dstOffset [ 1 ] ;
if ( dstExtent [ 2 ] = = ~ 0u ) dstExtent [ 2 ] = dst - > info . depth - dstOffset [ 2 ] ;
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 " ) ;
lovrCheck ( texture - > info . usage & TEXTURE_TRANSFER , " Texture must be created with the 'transfer' usage to mipmap %s it " , " from " ) ;
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-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-06-04 21:28:23 +00:00
static void * tempGrow ( void * p , size_t size ) {
if ( size = = 0 ) return NULL ;
void * new = tempAlloc ( size ) ;
if ( ! p ) return new ;
return memcpy ( new , p , size > > 1 ) ;
}
2022-04-29 05:30:31 +00:00
static void beginFrame ( void ) {
if ( state . active ) {
return ;
}
state . active = true ;
state . tick = gpu_begin ( ) ;
2022-06-12 05:55:43 +00:00
state . stream = gpu_stream_begin ( " Internal uploads " ) ;
2022-06-19 00:43:12 +00:00
state . allocator . cursor = 0 ;
2022-06-18 00:43:26 +00:00
arr_clear ( & state . passes ) ;
2022-04-29 05:30:31 +00:00
}
2022-06-21 01:26:15 +00:00
// Clean up ALL passes created during the frame, even unsubmitted ones
static void cleanupPasses ( void ) {
for ( size_t i = 0 ; i < state . passes . length ; i + + ) {
Pass * pass = state . passes . data [ i ] ;
for ( size_t j = 0 ; j < pass - > access . length ; j + + ) {
Access * access = & pass - > access . data [ j ] ;
lovrRelease ( access - > buffer , lovrBufferDestroy ) ;
lovrRelease ( access - > texture , lovrTextureDestroy ) ;
}
for ( size_t j = 0 ; j < = pass - > pipelineIndex ; j + + ) {
lovrRelease ( pass - > pipelines [ j ] . sampler , lovrSamplerDestroy ) ;
lovrRelease ( pass - > pipelines [ j ] . shader , lovrShaderDestroy ) ;
lovrRelease ( pass - > pipelines [ j ] . material , lovrMaterialDestroy ) ;
pass - > pipelines [ j ] . sampler = NULL ;
pass - > pipelines [ j ] . shader = NULL ;
pass - > pipelines [ j ] . material = NULL ;
}
}
}
2022-05-24 04:40:57 +00:00
static uint32_t getLayout ( gpu_slot * slots , uint32_t count ) {
uint64_t hash = hash64 ( slots , count * sizeof ( gpu_slot ) ) ;
uint32_t index ;
for ( uint32_t index = 0 ; index < state . layouts . length ; index + + ) {
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-05-30 19:17:17 +00:00
static gpu_bundle * getBundle ( uint32_t layoutIndex ) {
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 ;
if ( pool & & gpu_finished ( pool - > tick ) ) {
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-06-10 03:05:31 +00:00
// Note that we are technically not doing synchronization correctly for temporary attachments. It
// is very unlikely to be a problem in practice, though it should still be fixed for correctness.
2022-05-11 22:28:04 +00:00
static gpu_texture * getAttachment ( uint32_t size [ 2 ] , uint32_t layers , TextureFormat format , bool srgb , uint32_t samples ) {
2022-05-11 19:51:13 +00:00
uint16_t key [ ] = { size [ 0 ] , size [ 1 ] , layers , format , srgb , samples } ;
2022-06-10 03:05:31 +00:00
uint32_t hash = ( uint32_t ) hash64 ( key , sizeof ( key ) ) ;
// Find a matching attachment that hasn't been used this frame
for ( uint32_t i = 0 ; i < state . attachments . length ; i + + ) {
if ( state . attachments . data [ i ] . hash = = hash & & state . attachments . data [ i ] . tick ! = state . tick ) {
return state . attachments . data [ i ] . texture ;
}
}
2022-05-11 19:51:13 +00:00
2022-06-10 03:05:31 +00:00
// Find something to evict
TempAttachment * attachment = NULL ;
for ( uint32_t i = 0 ; i < state . attachments . length ; i + + ) {
if ( state . tick - state . attachments . data [ i ] . tick > 16 ) {
attachment = & state . attachments . data [ i ] ;
break ;
2022-05-11 19:51:13 +00:00
}
}
2022-06-10 03:05:31 +00:00
if ( attachment ) {
gpu_texture_destroy ( attachment - > texture ) ;
} else {
arr_expand ( & state . attachments , 1 ) ;
attachment = & state . attachments . data [ state . attachments . length + + ] ;
attachment - > texture = calloc ( 1 , gpu_sizeof_texture ( ) ) ;
lovrAssert ( attachment - > texture , " Out of memory " ) ;
}
2022-05-11 19:51:13 +00:00
gpu_texture_info info = {
. type = GPU_TEXTURE_ARRAY ,
. format = ( gpu_texture_format ) format ,
. size [ 0 ] = size [ 0 ] ,
. size [ 1 ] = size [ 1 ] ,
. size [ 2 ] = layers ,
. mipmaps = 1 ,
. samples = samples ,
. usage = GPU_TEXTURE_RENDER | GPU_TEXTURE_TRANSIENT ,
. srgb = srgb
} ;
2022-05-11 22:28:04 +00:00
lovrAssert ( gpu_texture_init ( attachment - > texture , & info ) , " Failed to create scratch texture " ) ;
attachment - > hash = hash ;
attachment - > tick = state . tick ;
return attachment - > texture ;
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
static size_t measureTexture ( TextureFormat format , uint16_t w , uint16_t h , uint16_t d ) {
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 ;
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 ) ;
uint32_t maxDepth = info - > type = = TEXTURE_3D ? MAX ( info - > depth > > offset [ 3 ] , 1 ) : info - > depth ;
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 ) ;
lovrCheck ( offset [ 2 ] + extent [ 2 ] < = maxDepth , " Texture z range [%d,%d] exceeds depth (%d) " , offset [ 2 ] , offset [ 2 ] + extent [ 2 ] , maxDepth ) ;
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 ) ,
volumetric ? MAX ( texture - > info . depth > > ( level - 1 ) , 1 ) : 1
} ;
uint32_t dstExtent [ 3 ] = {
MAX ( texture - > info . width > > level , 1 ) ,
MAX ( texture - > info . height > > level , 1 ) ,
volumetric ? MAX ( texture - > info . depth > > level , 1 ) : 1
} ;
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-06-12 05:55:43 +00:00
if ( texture = = state . window ) {
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-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 ) ;
}
}