2018-07-17 10:35:01 +00:00
# include "graphics/graphics.h"
2018-12-07 00:14:30 +00:00
# include "graphics/buffer.h"
2018-07-17 10:35:01 +00:00
# include "graphics/canvas.h"
2019-04-05 11:58:29 +00:00
# include "graphics/material.h"
2018-07-17 10:35:01 +00:00
# include "graphics/mesh.h"
# include "graphics/shader.h"
# include "graphics/texture.h"
2018-07-17 06:51:11 +00:00
# include "resources/shaders.h"
2021-02-08 18:16:00 +00:00
# include "data/blob.h"
2018-07-17 06:51:11 +00:00
# include "data/modelData.h"
2020-02-23 06:42:05 +00:00
# include "math/math.h"
2018-07-17 06:51:11 +00:00
# include <math.h>
# include <limits.h>
# include <string.h>
# include <stdlib.h>
2019-01-25 01:39:27 +00:00
# include <stdio.h>
2018-07-17 06:51:11 +00:00
2020-02-23 07:18:54 +00:00
# ifdef LOVR_WEBGL
# include <GLES3/gl3.h>
# include <GLES2/gl2ext.h>
# include <GL/gl.h>
# include <GL/glext.h>
# else
# include "lib/glad/glad.h"
# endif
2018-07-17 10:35:01 +00:00
// Types
2018-07-17 06:51:11 +00:00
# define MAX_TEXTURES 16
2018-08-10 10:37:02 +00:00
# define MAX_IMAGES 8
2018-08-03 18:34:11 +00:00
# define MAX_BLOCK_BUFFERS 8
2018-07-17 06:51:11 +00:00
# define LOVR_SHADER_POSITION 0
# define LOVR_SHADER_NORMAL 1
# define LOVR_SHADER_TEX_COORD 2
# define LOVR_SHADER_VERTEX_COLOR 3
# define LOVR_SHADER_TANGENT 4
# define LOVR_SHADER_BONES 5
# define LOVR_SHADER_BONE_WEIGHTS 6
2018-12-27 21:50:42 +00:00
# define LOVR_SHADER_DRAW_ID 7
2018-07-17 06:51:11 +00:00
2020-02-23 05:43:36 +00:00
struct Buffer {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 05:43:36 +00:00
uint32_t id ;
void * data ;
size_t size ;
size_t flushFrom ;
size_t flushTo ;
BufferType type ;
BufferUsage usage ;
bool mapped ;
bool readable ;
uint8_t incoherent ;
} ;
2020-02-23 06:03:20 +00:00
struct Texture {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 06:03:20 +00:00
GLuint id ;
GLuint msaaId ;
GLenum target ;
TextureType type ;
TextureFormat format ;
uint32_t width ;
uint32_t height ;
uint32_t depth ;
uint32_t mipmapCount ;
CompareMode compareMode ;
TextureFilter filter ;
TextureWrap wrap ;
uint32_t msaa ;
bool srgb ;
bool mipmaps ;
bool allocated ;
bool native ;
uint8_t incoherent ;
} ;
2020-02-23 06:19:59 +00:00
struct Canvas {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 06:19:59 +00:00
uint32_t framebuffer ;
uint32_t resolveBuffer ;
uint32_t depthBuffer ;
uint32_t width ;
uint32_t height ;
CanvasFlags flags ;
Attachment attachments [ MAX_CANVAS_ATTACHMENTS ] ;
Attachment depth ;
uint32_t attachmentCount ;
bool needsAttach ;
bool needsResolve ;
bool immortal ;
} ;
2020-02-23 06:42:05 +00:00
struct ShaderBlock {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 06:42:05 +00:00
BlockType type ;
arr_uniform_t uniforms ;
map_t uniformMap ;
struct Buffer * buffer ;
} ;
struct Shader {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 06:42:05 +00:00
uint32_t program ;
ShaderType type ;
arr_uniform_t uniforms ;
arr_block_t blocks [ 2 ] ;
map_t attributes ;
map_t uniformMap ;
map_t blockMap ;
bool multiview ;
} ;
2020-02-23 07:18:54 +00:00
struct Mesh {
2021-02-11 23:37:55 +00:00
uint32_t ref ;
2020-02-23 07:18:54 +00:00
uint32_t vao ;
uint32_t ibo ;
DrawMode mode ;
char attributeNames [ MAX_ATTRIBUTES ] [ MAX_ATTRIBUTE_NAME_LENGTH ] ;
MeshAttribute attributes [ MAX_ATTRIBUTES ] ;
uint8_t locations [ MAX_ATTRIBUTES ] ;
uint16_t enabledLocations ;
uint16_t divisors [ MAX_ATTRIBUTES ] ;
map_t attributeMap ;
uint32_t attributeCount ;
struct Buffer * vertexBuffer ;
struct Buffer * indexBuffer ;
uint32_t vertexCount ;
uint32_t indexCount ;
size_t indexSize ;
size_t indexOffset ;
uint32_t drawStart ;
uint32_t drawCount ;
struct Material * material ;
} ;
2018-08-31 13:03:35 +00:00
typedef enum {
BARRIER_BLOCK ,
BARRIER_UNIFORM_TEXTURE ,
BARRIER_UNIFORM_IMAGE ,
BARRIER_TEXTURE ,
BARRIER_CANVAS ,
MAX_BARRIERS
} Barrier ;
2018-12-27 21:50:42 +00:00
typedef struct {
uint32_t buffer ;
size_t offset ;
size_t size ;
} BlockBuffer ;
2019-05-26 11:37:00 +00:00
typedef struct {
2019-09-07 22:07:07 +00:00
GLuint * queries ;
uint32_t * chain ;
uint32_t next ;
uint32_t count ;
} QueryPool ;
typedef struct {
uint32_t head ;
uint32_t tail ;
uint64_t nanoseconds ;
} Timer ;
2019-05-26 11:37:00 +00:00
2018-07-17 10:35:01 +00:00
static struct {
Texture * defaultTexture ;
2019-06-25 08:06:55 +00:00
enum { NONE , INSTANCED_STEREO , MULTIVIEW } singlepass ;
2018-12-12 06:52:33 +00:00
bool alphaToCoverage ;
2018-12-27 21:50:42 +00:00
bool blendEnabled ;
2018-07-17 10:35:01 +00:00
BlendMode blendMode ;
BlendAlphaMode blendAlphaMode ;
2019-12-10 21:44:51 +00:00
uint8_t colorMask ;
2018-07-17 10:35:01 +00:00
bool culling ;
bool depthEnabled ;
CompareMode depthTest ;
bool depthWrite ;
2019-12-10 21:44:51 +00:00
float lineWidth ;
2019-01-03 09:21:38 +00:00
uint32_t primitiveRestart ;
2018-07-17 10:35:01 +00:00
bool stencilEnabled ;
CompareMode stencilMode ;
int stencilValue ;
bool stencilWriting ;
Winding winding ;
bool wireframe ;
uint32_t framebuffer ;
uint32_t program ;
2019-02-01 01:24:13 +00:00
Mesh * vertexArray ;
2018-12-27 21:50:42 +00:00
uint32_t buffers [ MAX_BUFFER_TYPES ] ;
BlockBuffer blockBuffers [ 2 ] [ MAX_BLOCK_BUFFERS ] ;
2018-08-31 02:42:01 +00:00
int activeTexture ;
2018-07-17 10:35:01 +00:00
Texture * textures [ MAX_TEXTURES ] ;
2021-02-09 03:17:47 +00:00
StorageImage images [ MAX_IMAGES ] ;
2018-08-30 10:25:16 +00:00
float viewports [ 2 ] [ 4 ] ;
2019-01-04 04:07:45 +00:00
uint32_t viewportCount ;
2019-08-22 06:16:08 +00:00
arr_t ( void * ) incoherents [ MAX_BARRIERS ] ;
2019-09-07 22:07:07 +00:00
QueryPool queryPool ;
arr_t ( Timer ) timers ;
uint32_t activeTimer ;
map_t timerMap ;
2018-08-31 13:03:35 +00:00
GpuFeatures features ;
GpuLimits limits ;
GpuStats stats ;
2021-04-13 16:52:28 +00:00
bool amd ;
2018-07-17 10:35:01 +00:00
} state ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
// Helper functions
2018-07-17 06:51:11 +00:00
static GLenum convertCompareMode ( CompareMode mode ) {
switch ( mode ) {
case COMPARE_NONE : return GL_ALWAYS ;
case COMPARE_EQUAL : return GL_EQUAL ;
case COMPARE_NEQUAL : return GL_NOTEQUAL ;
case COMPARE_LESS : return GL_LESS ;
case COMPARE_LEQUAL : return GL_LEQUAL ;
case COMPARE_GREATER : return GL_GREATER ;
case COMPARE_GEQUAL : return GL_GEQUAL ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
}
2018-07-17 10:35:01 +00:00
static GLenum convertWrapMode ( WrapMode mode ) {
2018-07-17 06:51:11 +00:00
switch ( mode ) {
case WRAP_CLAMP : return GL_CLAMP_TO_EDGE ;
case WRAP_REPEAT : return GL_REPEAT ;
case WRAP_MIRRORED_REPEAT : return GL_MIRRORED_REPEAT ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
}
2018-09-09 05:28:30 +00:00
static GLenum convertTextureTarget ( TextureType type ) {
switch ( type ) {
case TEXTURE_2D : return GL_TEXTURE_2D ; break ;
case TEXTURE_ARRAY : return GL_TEXTURE_2D_ARRAY ; break ;
case TEXTURE_CUBE : return GL_TEXTURE_CUBE_MAP ; break ;
case TEXTURE_VOLUME : return GL_TEXTURE_3D ; break ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-09-09 05:28:30 +00:00
}
}
2018-07-17 10:35:01 +00:00
static GLenum convertTextureFormat ( TextureFormat format ) {
2018-07-17 06:51:11 +00:00
switch ( format ) {
case FORMAT_RGB : return GL_RGB ;
case FORMAT_RGBA : return GL_RGBA ;
2018-07-21 00:45:25 +00:00
case FORMAT_RGBA4 : return GL_RGBA ;
2020-05-10 07:59:01 +00:00
case FORMAT_R16 : return GL_RED ;
case FORMAT_RG16 : return GL_RG ;
case FORMAT_RGBA16 : return GL_RGBA ;
2018-07-17 06:51:11 +00:00
case FORMAT_RGBA16F : return GL_RGBA ;
case FORMAT_RGBA32F : return GL_RGBA ;
2018-07-21 00:45:25 +00:00
case FORMAT_R16F : return GL_RED ;
case FORMAT_R32F : return GL_RED ;
2018-08-16 21:54:37 +00:00
case FORMAT_RG16F : return GL_RG ;
case FORMAT_RG32F : return GL_RG ;
2018-07-21 00:45:25 +00:00
case FORMAT_RGB5A1 : return GL_RGBA ;
case FORMAT_RGB10A2 : return GL_RGBA ;
2018-07-17 06:51:11 +00:00
case FORMAT_RG11B10F : return GL_RGB ;
2018-09-28 01:34:43 +00:00
case FORMAT_D16 : return GL_DEPTH_COMPONENT ;
case FORMAT_D32F : return GL_DEPTH_COMPONENT ;
case FORMAT_D24S8 : return GL_DEPTH_STENCIL ;
2018-07-17 06:51:11 +00:00
case FORMAT_DXT1 : return GL_COMPRESSED_RGB_S3TC_DXT1_EXT ;
case FORMAT_DXT3 : return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ;
case FORMAT_DXT5 : return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ;
2019-06-09 16:11:29 +00:00
case FORMAT_ASTC_4x4 :
case FORMAT_ASTC_5x4 :
case FORMAT_ASTC_5x5 :
case FORMAT_ASTC_6x5 :
case FORMAT_ASTC_6x6 :
case FORMAT_ASTC_8x5 :
case FORMAT_ASTC_8x6 :
case FORMAT_ASTC_8x8 :
case FORMAT_ASTC_10x5 :
case FORMAT_ASTC_10x6 :
case FORMAT_ASTC_10x8 :
case FORMAT_ASTC_10x10 :
case FORMAT_ASTC_12x10 :
case FORMAT_ASTC_12x12 :
return GL_RGBA ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
}
2018-07-17 10:35:01 +00:00
static GLenum convertTextureFormatInternal ( TextureFormat format , bool srgb ) {
2018-07-17 06:51:11 +00:00
switch ( format ) {
case FORMAT_RGB : return srgb ? GL_SRGB8 : GL_RGB8 ;
case FORMAT_RGBA : return srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8 ;
2018-07-21 00:45:25 +00:00
case FORMAT_RGBA4 : return GL_RGBA4 ;
2020-05-10 07:59:01 +00:00
case FORMAT_R16 : return GL_R16 ;
case FORMAT_RG16 : return GL_RG16 ;
case FORMAT_RGBA16 : return GL_RGBA16 ;
2018-07-17 06:51:11 +00:00
case FORMAT_RGBA16F : return GL_RGBA16F ;
case FORMAT_RGBA32F : return GL_RGBA32F ;
2018-07-21 00:45:25 +00:00
case FORMAT_R16F : return GL_R16F ;
case FORMAT_R32F : return GL_R32F ;
2018-08-16 21:54:37 +00:00
case FORMAT_RG16F : return GL_RG16F ;
case FORMAT_RG32F : return GL_RG32F ;
2018-07-21 00:45:25 +00:00
case FORMAT_RGB5A1 : return GL_RGB5_A1 ;
case FORMAT_RGB10A2 : return GL_RGB10_A2 ;
2018-07-17 06:51:11 +00:00
case FORMAT_RG11B10F : return GL_R11F_G11F_B10F ;
2018-09-28 01:34:43 +00:00
case FORMAT_D16 : return GL_DEPTH_COMPONENT16 ;
case FORMAT_D32F : return GL_DEPTH_COMPONENT32F ;
case FORMAT_D24S8 : return GL_DEPTH24_STENCIL8 ;
2018-07-17 06:51:11 +00:00
case FORMAT_DXT1 : return srgb ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT ;
case FORMAT_DXT3 : return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ;
case FORMAT_DXT5 : return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ;
2019-07-29 00:28:07 +00:00
# ifdef LOVR_WEBGL
case FORMAT_ASTC_4x4 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR ;
case FORMAT_ASTC_5x4 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR ;
case FORMAT_ASTC_5x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR ;
case FORMAT_ASTC_6x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR ;
case FORMAT_ASTC_6x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR ;
case FORMAT_ASTC_8x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR ;
case FORMAT_ASTC_8x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR ;
case FORMAT_ASTC_8x8 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR ;
case FORMAT_ASTC_10x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR ;
case FORMAT_ASTC_10x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : GL_COMPRESSED_RGBA_ASTC_10x6_KHR ;
case FORMAT_ASTC_10x8 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : GL_COMPRESSED_RGBA_ASTC_10x8_KHR ;
case FORMAT_ASTC_10x10 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : GL_COMPRESSED_RGBA_ASTC_10x10_KHR ;
case FORMAT_ASTC_12x10 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : GL_COMPRESSED_RGBA_ASTC_12x10_KHR ;
case FORMAT_ASTC_12x12 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : GL_COMPRESSED_RGBA_ASTC_12x12_KHR ;
# else
2019-06-09 16:11:29 +00:00
case FORMAT_ASTC_4x4 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4 : GL_COMPRESSED_RGBA_ASTC_4x4 ;
case FORMAT_ASTC_5x4 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4 : GL_COMPRESSED_RGBA_ASTC_5x4 ;
case FORMAT_ASTC_5x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5 : GL_COMPRESSED_RGBA_ASTC_5x5 ;
case FORMAT_ASTC_6x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5 : GL_COMPRESSED_RGBA_ASTC_6x5 ;
case FORMAT_ASTC_6x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6 : GL_COMPRESSED_RGBA_ASTC_6x6 ;
case FORMAT_ASTC_8x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5 : GL_COMPRESSED_RGBA_ASTC_8x5 ;
case FORMAT_ASTC_8x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6 : GL_COMPRESSED_RGBA_ASTC_8x6 ;
case FORMAT_ASTC_8x8 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8 : GL_COMPRESSED_RGBA_ASTC_8x8 ;
case FORMAT_ASTC_10x5 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5 : GL_COMPRESSED_RGBA_ASTC_10x5 ;
case FORMAT_ASTC_10x6 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6 : GL_COMPRESSED_RGBA_ASTC_10x6 ;
case FORMAT_ASTC_10x8 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8 : GL_COMPRESSED_RGBA_ASTC_10x8 ;
case FORMAT_ASTC_10x10 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10 : GL_COMPRESSED_RGBA_ASTC_10x10 ;
case FORMAT_ASTC_12x10 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10 : GL_COMPRESSED_RGBA_ASTC_12x10 ;
case FORMAT_ASTC_12x12 : return srgb ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12 : GL_COMPRESSED_RGBA_ASTC_12x12 ;
2019-07-29 00:28:07 +00:00
# endif
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
}
2018-08-16 21:28:10 +00:00
static GLenum convertTextureFormatType ( TextureFormat format ) {
switch ( format ) {
case FORMAT_RGB : return GL_UNSIGNED_BYTE ;
case FORMAT_RGBA : return GL_UNSIGNED_BYTE ;
case FORMAT_RGBA4 : return GL_UNSIGNED_SHORT_4_4_4_4 ;
2020-05-10 07:59:01 +00:00
case FORMAT_R16 : return GL_UNSIGNED_SHORT ;
case FORMAT_RG16 : return GL_UNSIGNED_SHORT ;
case FORMAT_RGBA16 : return GL_UNSIGNED_SHORT ;
2018-08-16 21:28:10 +00:00
case FORMAT_RGBA16F : return GL_HALF_FLOAT ;
case FORMAT_RGBA32F : return GL_FLOAT ;
case FORMAT_R16F : return GL_HALF_FLOAT ;
case FORMAT_R32F : return GL_FLOAT ;
2018-08-16 21:54:37 +00:00
case FORMAT_RG16F : return GL_HALF_FLOAT ;
case FORMAT_RG32F : return GL_FLOAT ;
2018-08-16 21:28:10 +00:00
case FORMAT_RGB5A1 : return GL_UNSIGNED_SHORT_5_5_5_1 ;
case FORMAT_RGB10A2 : return GL_UNSIGNED_INT_2_10_10_10_REV ;
case FORMAT_RG11B10F : return GL_UNSIGNED_INT_10F_11F_11F_REV ;
2018-09-28 01:34:43 +00:00
case FORMAT_D16 : return GL_UNSIGNED_SHORT ;
case FORMAT_D32F : return GL_UNSIGNED_INT ;
case FORMAT_D24S8 : return GL_UNSIGNED_INT_24_8 ;
2020-02-17 21:34:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-08-16 21:28:10 +00:00
}
}
2018-09-28 01:34:43 +00:00
static bool isTextureFormatCompressed ( TextureFormat format ) {
2018-08-24 21:43:39 +00:00
switch ( format ) {
2019-06-09 16:11:29 +00:00
case FORMAT_DXT1 :
case FORMAT_DXT3 :
case FORMAT_DXT5 :
case FORMAT_ASTC_4x4 :
case FORMAT_ASTC_5x4 :
case FORMAT_ASTC_5x5 :
case FORMAT_ASTC_6x5 :
case FORMAT_ASTC_6x6 :
case FORMAT_ASTC_8x5 :
case FORMAT_ASTC_8x6 :
case FORMAT_ASTC_8x8 :
case FORMAT_ASTC_10x5 :
case FORMAT_ASTC_10x6 :
case FORMAT_ASTC_10x8 :
case FORMAT_ASTC_10x10 :
case FORMAT_ASTC_12x10 :
case FORMAT_ASTC_12x12 :
return true ;
default :
return false ;
2018-08-24 21:43:39 +00:00
}
}
2018-09-28 01:34:43 +00:00
static bool isTextureFormatDepth ( TextureFormat format ) {
2018-07-17 06:51:11 +00:00
switch ( format ) {
2018-09-28 01:34:43 +00:00
case FORMAT_D16 : case FORMAT_D32F : case FORMAT_D24S8 : return true ;
2018-08-31 02:40:31 +00:00
default : return false ;
2018-07-17 06:51:11 +00:00
}
}
2020-01-31 11:35:42 +00:00
static uint64_t getTextureMemorySize ( Texture * texture ) {
if ( texture - > native ) return 0 ;
float size = 0.f ;
float bitrate ;
switch ( texture - > format ) {
case FORMAT_RGB : bitrate = 24.f ; break ;
case FORMAT_RGBA : bitrate = 32.f ; break ;
case FORMAT_RGBA4 : bitrate = 16.f ; break ;
2020-05-10 07:59:01 +00:00
case FORMAT_R16 : bitrate = 16.f ; break ;
case FORMAT_RG16 : bitrate = 32.f ; break ;
case FORMAT_RGBA16 : bitrate = 64.f ; break ;
2020-01-31 11:35:42 +00:00
case FORMAT_RGBA16F : bitrate = 64.f ; break ;
case FORMAT_RGBA32F : bitrate = 128.f ; break ;
case FORMAT_R16F : bitrate = 16.f ; break ;
case FORMAT_R32F : bitrate = 32.f ; break ;
case FORMAT_RG16F : bitrate = 32.f ; break ;
case FORMAT_RG32F : bitrate = 64.f ; break ;
case FORMAT_RGB5A1 : bitrate = 16.f ; break ;
case FORMAT_RGB10A2 : bitrate = 32.f ; break ;
case FORMAT_RG11B10F : bitrate = 32.f ; break ;
case FORMAT_D16 : bitrate = 16.f ; break ;
case FORMAT_D32F : bitrate = 32.f ; break ;
case FORMAT_D24S8 : bitrate = 32.f ; break ;
case FORMAT_DXT1 : bitrate = 4.f ; break ;
case FORMAT_DXT3 : bitrate = 8.f ; break ;
case FORMAT_DXT5 : bitrate = 8.f ; break ;
// Divide fixed-size 128-bit blocks by block size:
case FORMAT_ASTC_4x4 : bitrate = 8.00f ; break ;
case FORMAT_ASTC_5x4 : bitrate = 6.40f ; break ;
case FORMAT_ASTC_5x5 : bitrate = 5.12f ; break ;
case FORMAT_ASTC_6x5 : bitrate = 4.27f ; break ;
case FORMAT_ASTC_6x6 : bitrate = 3.56f ; break ;
case FORMAT_ASTC_8x5 : bitrate = 3.20f ; break ;
case FORMAT_ASTC_8x6 : bitrate = 2.67f ; break ;
case FORMAT_ASTC_8x8 : bitrate = 2.00f ; break ;
case FORMAT_ASTC_10x5 : bitrate = 2.56f ; break ;
case FORMAT_ASTC_10x6 : bitrate = 2.13f ; break ;
case FORMAT_ASTC_10x8 : bitrate = 1.60f ; break ;
case FORMAT_ASTC_10x10 : bitrate = 1.28f ; break ;
case FORMAT_ASTC_12x10 : bitrate = 1.07f ; break ;
case FORMAT_ASTC_12x12 : bitrate = 0.89f ; break ;
default : lovrThrow ( " Unreachable " ) ;
}
size = texture - > width * texture - > height * texture - > depth * ( bitrate / 8.f ) * ( texture - > mipmaps ? 1.33f : 1.f ) ;
size + = texture - > msaa > 1 ? ( texture - > width * texture - > height * texture - > msaa * ( bitrate / 8.f ) ) : 0.f ;
return ( uint64_t ) ( size + .5f ) ;
}
2018-09-26 00:10:09 +00:00
static GLenum convertAttributeType ( AttributeType type ) {
switch ( type ) {
case I8 : return GL_BYTE ;
case U8 : return GL_UNSIGNED_BYTE ;
case I16 : return GL_SHORT ;
case U16 : return GL_UNSIGNED_SHORT ;
case I32 : return GL_INT ;
case U32 : return GL_UNSIGNED_INT ;
case F32 : return GL_FLOAT ;
default : lovrThrow ( " Unreachable " ) ;
}
}
2018-12-27 21:50:42 +00:00
static GLenum convertBufferType ( BufferType type ) {
switch ( type ) {
case BUFFER_VERTEX : return GL_ARRAY_BUFFER ;
case BUFFER_INDEX : return GL_ELEMENT_ARRAY_BUFFER ;
case BUFFER_UNIFORM : return GL_UNIFORM_BUFFER ;
case BUFFER_SHADER_STORAGE : return GL_SHADER_STORAGE_BUFFER ;
case BUFFER_GENERIC : return GL_COPY_WRITE_BUFFER ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-12-27 21:50:42 +00:00
}
}
2018-08-02 04:05:30 +00:00
static GLenum convertBufferUsage ( BufferUsage usage ) {
2018-07-17 10:35:01 +00:00
switch ( usage ) {
2018-08-02 04:05:30 +00:00
case USAGE_STATIC : return GL_STATIC_DRAW ;
case USAGE_DYNAMIC : return GL_DYNAMIC_DRAW ;
case USAGE_STREAM : return GL_STREAM_DRAW ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-08-17 22:01:11 +00:00
static GLenum convertAccess ( UniformAccess access ) {
switch ( access ) {
case ACCESS_READ : return GL_READ_ONLY ;
case ACCESS_WRITE : return GL_WRITE_ONLY ;
case ACCESS_READ_WRITE : return GL_READ_WRITE ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-08-17 22:01:11 +00:00
}
}
2018-08-31 03:56:08 +00:00
# endif
2018-08-17 22:01:11 +00:00
2019-06-28 03:58:42 +00:00
static GLenum convertTopology ( DrawMode topology ) {
switch ( topology ) {
2018-12-13 02:43:04 +00:00
case DRAW_POINTS : return GL_POINTS ;
case DRAW_LINES : return GL_LINES ;
case DRAW_LINE_STRIP : return GL_LINE_STRIP ;
case DRAW_LINE_LOOP : return GL_LINE_LOOP ;
case DRAW_TRIANGLE_STRIP : return GL_TRIANGLE_STRIP ;
case DRAW_TRIANGLES : return GL_TRIANGLES ;
case DRAW_TRIANGLE_FAN : return GL_TRIANGLE_FAN ;
2019-02-17 07:43:20 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2020-05-21 06:24:19 +00:00
static bool isAttributeTypeInteger ( GLenum type ) {
switch ( type ) {
case GL_INT :
case GL_INT_VEC2 :
case GL_INT_VEC3 :
case GL_INT_VEC4 :
case GL_UNSIGNED_INT :
case GL_UNSIGNED_INT_VEC2 :
case GL_UNSIGNED_INT_VEC3 :
case GL_UNSIGNED_INT_VEC4 :
return true ;
default :
return false ;
}
}
2018-07-17 10:35:01 +00:00
static UniformType getUniformType ( GLenum type , const char * debug ) {
switch ( type ) {
case GL_FLOAT :
case GL_FLOAT_VEC2 :
case GL_FLOAT_VEC3 :
case GL_FLOAT_VEC4 :
return UNIFORM_FLOAT ;
case GL_INT :
case GL_INT_VEC2 :
case GL_INT_VEC3 :
case GL_INT_VEC4 :
return UNIFORM_INT ;
case GL_FLOAT_MAT2 :
case GL_FLOAT_MAT3 :
case GL_FLOAT_MAT4 :
return UNIFORM_MATRIX ;
case GL_SAMPLER_2D :
case GL_SAMPLER_3D :
case GL_SAMPLER_CUBE :
case GL_SAMPLER_2D_ARRAY :
2019-08-29 21:26:44 +00:00
case GL_SAMPLER_2D_SHADOW :
2018-08-18 02:52:34 +00:00
return UNIFORM_SAMPLER ;
2018-08-10 10:37:02 +00:00
# ifdef GL_ARB_shader_image_load_store
case GL_IMAGE_2D :
case GL_IMAGE_3D :
case GL_IMAGE_CUBE :
case GL_IMAGE_2D_ARRAY :
2018-08-18 02:52:34 +00:00
return UNIFORM_IMAGE ;
2018-08-10 10:37:02 +00:00
# endif
2018-07-17 10:35:01 +00:00
default :
2018-08-03 18:19:40 +00:00
lovrThrow ( " Unsupported uniform type for uniform '%s' " , debug ) ;
2018-07-17 10:35:01 +00:00
return UNIFORM_FLOAT ;
}
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
static int getUniformComponents ( GLenum type ) {
2018-07-17 06:51:11 +00:00
switch ( type ) {
2018-08-31 02:40:31 +00:00
case GL_FLOAT_VEC2 : case GL_INT_VEC2 : case GL_FLOAT_MAT2 : return 2 ;
case GL_FLOAT_VEC3 : case GL_INT_VEC3 : case GL_FLOAT_MAT3 : return 3 ;
case GL_FLOAT_VEC4 : case GL_INT_VEC4 : case GL_FLOAT_MAT4 : return 4 ;
default : return 1 ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-08-10 02:25:51 +00:00
static TextureType getUniformTextureType ( GLenum type ) {
switch ( type ) {
2018-08-18 02:52:34 +00:00
case GL_SAMPLER_2D : return TEXTURE_2D ;
case GL_SAMPLER_3D : return TEXTURE_VOLUME ;
case GL_SAMPLER_CUBE : return TEXTURE_CUBE ;
case GL_SAMPLER_2D_ARRAY : return TEXTURE_ARRAY ;
2019-08-29 21:26:44 +00:00
case GL_SAMPLER_2D_SHADOW : return TEXTURE_2D ;
2018-08-18 02:52:34 +00:00
# ifdef GL_ARB_shader_image_load_store
case GL_IMAGE_2D : return TEXTURE_2D ;
case GL_IMAGE_3D : return TEXTURE_VOLUME ;
case GL_IMAGE_CUBE : return TEXTURE_CUBE ;
case GL_IMAGE_2D_ARRAY : return TEXTURE_ARRAY ;
# endif
2018-08-10 02:25:51 +00:00
default : return - 1 ;
}
}
2020-02-23 06:42:05 +00:00
static size_t getUniformTypeLength ( const Uniform * uniform ) {
size_t size = 0 ;
if ( uniform - > count > 1 ) {
size + = 2 + floor ( log10 ( uniform - > count ) ) + 1 ; // "[count]"
}
switch ( uniform - > type ) {
case UNIFORM_MATRIX : size + = 4 ; break ;
case UNIFORM_FLOAT : size + = uniform - > components = = 1 ? 5 : 4 ; break ;
case UNIFORM_INT : size + = uniform - > components = = 1 ? 3 : 5 ; break ;
default : break ;
}
return size ;
}
static const char * getUniformTypeName ( const Uniform * uniform ) {
switch ( uniform - > type ) {
case UNIFORM_FLOAT :
switch ( uniform - > components ) {
case 1 : return " float " ;
case 2 : return " vec2 " ;
case 3 : return " vec3 " ;
case 4 : return " vec4 " ;
}
break ;
case UNIFORM_INT :
switch ( uniform - > components ) {
case 1 : return " int " ;
case 2 : return " ivec2 " ;
case 3 : return " ivec3 " ;
case 4 : return " ivec4 " ;
}
break ;
case UNIFORM_MATRIX :
switch ( uniform - > components ) {
case 2 : return " mat2 " ;
case 3 : return " mat3 " ;
case 4 : return " mat4 " ;
}
break ;
default : break ;
}
lovrThrow ( " Unreachable " ) ;
return " " ;
}
2018-08-31 13:03:35 +00:00
// Syncing resources is only relevant for compute shaders
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-08-31 13:03:35 +00:00
static void lovrGpuSync ( uint8_t flags ) {
if ( ! flags ) {
return ;
}
GLbitfield bits = 0 ;
for ( int i = 0 ; i < MAX_BARRIERS ; i + + ) {
if ( ! ( ( flags > > i ) & 1 ) ) {
continue ;
}
if ( state . incoherents [ i ] . length = = 0 ) {
flags & = ~ ( 1 < < i ) ;
continue ;
}
if ( i = = BARRIER_BLOCK ) {
2019-06-08 10:45:03 +00:00
for ( size_t j = 0 ; j < state . incoherents [ i ] . length ; j + + ) {
2018-12-27 21:50:42 +00:00
Buffer * buffer = state . incoherents [ i ] . data [ j ] ;
buffer - > incoherent & = ~ ( 1 < < i ) ;
2018-08-31 13:03:35 +00:00
}
} else {
2019-06-08 10:45:03 +00:00
for ( size_t j = 0 ; j < state . incoherents [ i ] . length ; j + + ) {
2018-08-31 13:03:35 +00:00
Texture * texture = state . incoherents [ i ] . data [ j ] ;
texture - > incoherent & = ~ ( 1 < < i ) ;
}
}
2019-06-08 10:45:03 +00:00
arr_clear ( & state . incoherents [ i ] ) ;
2018-09-03 05:20:32 +00:00
2018-08-31 13:03:35 +00:00
switch ( i ) {
case BARRIER_BLOCK : bits | = GL_SHADER_STORAGE_BARRIER_BIT ; break ;
case BARRIER_UNIFORM_IMAGE : bits | = GL_SHADER_IMAGE_ACCESS_BARRIER_BIT ; break ;
case BARRIER_UNIFORM_TEXTURE : bits | = GL_TEXTURE_FETCH_BARRIER_BIT ; break ;
case BARRIER_TEXTURE : bits | = GL_TEXTURE_UPDATE_BARRIER_BIT ; break ;
case BARRIER_CANVAS : bits | = GL_FRAMEBUFFER_BARRIER_BIT ; break ;
}
}
if ( bits ) {
glMemoryBarrier ( bits ) ;
}
}
# endif
static void lovrGpuDestroySyncResource ( void * resource , uint8_t incoherent ) {
2018-08-12 04:17:22 +00:00
if ( ! incoherent ) {
return ;
}
2019-06-08 10:45:03 +00:00
for ( uint32_t i = 0 ; i < MAX_BARRIERS ; i + + ) {
2018-08-12 04:17:22 +00:00
if ( incoherent & ( 1 < < i ) ) {
2019-06-08 10:45:03 +00:00
for ( size_t j = 0 ; j < state . incoherents [ i ] . length ; j + + ) {
2018-08-12 04:17:22 +00:00
if ( state . incoherents [ i ] . data [ j ] = = resource ) {
2019-06-08 10:45:03 +00:00
arr_splice ( & state . incoherents [ i ] , j , 1 ) ;
2018-08-12 04:17:22 +00:00
break ;
}
}
}
}
}
2018-10-26 05:05:28 +00:00
static void lovrGpuBindFramebuffer ( uint32_t framebuffer ) {
2018-08-24 01:28:37 +00:00
if ( state . framebuffer ! = framebuffer ) {
state . framebuffer = framebuffer ;
glBindFramebuffer ( GL_FRAMEBUFFER , framebuffer ) ;
2020-01-31 11:35:42 +00:00
state . stats . renderPasses + + ;
2018-08-24 01:28:37 +00:00
}
}
2018-12-27 21:50:42 +00:00
static void lovrGpuUseProgram ( uint32_t program ) {
if ( state . program ! = program ) {
state . program = program ;
glUseProgram ( program ) ;
state . stats . shaderSwitches + + ;
}
}
2019-02-01 01:24:13 +00:00
static void lovrGpuBindVertexArray ( Mesh * vertexArray ) {
2018-12-27 21:50:42 +00:00
if ( state . vertexArray ! = vertexArray ) {
state . vertexArray = vertexArray ;
2019-02-01 01:24:13 +00:00
glBindVertexArray ( vertexArray - > vao ) ;
2018-12-27 21:50:42 +00:00
}
}
2019-02-01 01:24:13 +00:00
static void lovrGpuBindBuffer ( BufferType type , uint32_t buffer ) {
if ( type = = BUFFER_INDEX & & state . vertexArray ) {
if ( buffer ! = state . vertexArray - > ibo ) {
state . vertexArray - > ibo = buffer ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , buffer ) ;
}
} else {
if ( state . buffers [ type ] ! = buffer ) {
state . buffers [ type ] = buffer ;
glBindBuffer ( convertBufferType ( type ) , buffer ) ;
}
2018-12-27 21:50:42 +00:00
}
}
static void lovrGpuBindBlockBuffer ( BlockType type , uint32_t buffer , int slot , size_t offset , size_t size ) {
lovrAssert ( offset % state . limits . blockAlign = = 0 , " Block buffer offset must be aligned to %d " , state . limits . blockAlign ) ;
2019-01-29 23:05:23 +00:00
# ifdef LOVR_WEBGL
2019-01-16 16:52:21 +00:00
lovrAssert ( type = = BLOCK_UNIFORM , " Compute blocks are not supported on this system " ) ;
2018-12-27 21:50:42 +00:00
GLenum target = GL_UNIFORM_BUFFER ;
# else
GLenum target = type = = BLOCK_UNIFORM ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER ;
# endif
BlockBuffer * block = & state . blockBuffers [ type ] [ slot ] ;
if ( block - > buffer ! = buffer | | block - > offset ! = offset | | block - > size ! = size ) {
block - > buffer = buffer ;
block - > offset = offset ;
block - > size = size ;
glBindBufferRange ( target , slot , buffer , offset , size ) ;
2019-06-25 08:06:55 +00:00
// Binding to an indexed target also binds to the generic target
BufferType bufferType = type = = BLOCK_UNIFORM ? BUFFER_UNIFORM : BUFFER_SHADER_STORAGE ;
state . buffers [ bufferType ] = buffer ;
2018-07-17 10:35:01 +00:00
}
}
2018-07-17 06:51:11 +00:00
2018-08-10 10:37:02 +00:00
static void lovrGpuBindTexture ( Texture * texture , int slot ) {
2018-07-17 10:35:01 +00:00
lovrAssert ( slot > = 0 & & slot < MAX_TEXTURES , " Invalid texture slot %d " , slot ) ;
2019-06-20 18:35:46 +00:00
texture = texture ? texture : state . defaultTexture ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
if ( texture ! = state . textures [ slot ] ) {
lovrRetain ( texture ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( state . textures [ slot ] , lovrTextureDestroy ) ;
2018-07-17 10:35:01 +00:00
state . textures [ slot ] = texture ;
2018-08-31 02:42:01 +00:00
if ( state . activeTexture ! = slot ) {
glActiveTexture ( GL_TEXTURE0 + slot ) ;
state . activeTexture = slot ;
}
2018-08-10 10:37:02 +00:00
glBindTexture ( texture - > target , texture - > id ) ;
2018-07-17 06:51:11 +00:00
}
}
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2021-02-09 03:17:47 +00:00
static void lovrGpuBindImage ( StorageImage * image , int slot , const char * name ) {
2018-08-10 10:37:02 +00:00
lovrAssert ( slot > = 0 & & slot < MAX_IMAGES , " Invalid image slot %d " , slot ) ;
2018-08-18 02:52:34 +00:00
// This is a risky way to compare the two structs
2021-02-09 03:17:47 +00:00
if ( memcmp ( state . images + slot , image , sizeof ( StorageImage ) ) ) {
2020-05-18 21:14:17 +00:00
Texture * texture = image - > texture ;
lovrAssert ( texture , " No Texture bound to image uniform '%s' " , name ) ;
2020-08-19 02:20:48 +00:00
lovrAssert ( texture - > format ! = FORMAT_RGBA | | ! texture - > srgb , " Attempt to bind sRGB texture to image uniform '%s' " , name ) ;
2020-05-18 21:14:17 +00:00
lovrAssert ( ! isTextureFormatCompressed ( texture - > format ) , " Attempt to bind compressed texture to image uniform '%s' " , name ) ;
lovrAssert ( texture - > format ! = FORMAT_RGB & & texture - > format ! = FORMAT_RGBA4 & & texture - > format ! = FORMAT_RGB5A1 , " Unsupported texture format for image uniform '%s' " , name ) ;
lovrAssert ( image - > mipmap < ( int ) texture - > mipmapCount , " Invalid mipmap level '%d' for image uniform '%s' " , image - > mipmap , name ) ;
lovrAssert ( image - > slice < ( int ) texture - > depth , " Invalid texture slice '%d' for image uniform '%s' " , image - > slice , name ) ;
2018-08-18 02:52:34 +00:00
GLenum glAccess = convertAccess ( image - > access ) ;
2018-08-17 22:01:11 +00:00
GLenum glFormat = convertTextureFormatInternal ( texture - > format , false ) ;
2018-09-04 13:58:54 +00:00
bool layered = image - > slice = = - 1 ;
int slice = layered ? 0 : image - > slice ;
2018-08-11 06:54:46 +00:00
2018-08-10 10:37:02 +00:00
lovrRetain ( texture ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( state . images [ slot ] . texture , lovrTextureDestroy ) ;
2018-09-04 13:58:54 +00:00
glBindImageTexture ( slot , texture - > id , image - > mipmap , layered , slice , glAccess , glFormat ) ;
2021-02-09 03:17:47 +00:00
memcpy ( state . images + slot , image , sizeof ( StorageImage ) ) ;
2018-08-10 10:37:02 +00:00
}
}
2018-08-31 13:03:35 +00:00
# endif
2018-08-10 10:37:02 +00:00
2019-01-27 22:29:06 +00:00
static void lovrGpuBindMesh ( Mesh * mesh , Shader * shader , int baseDivisor ) {
2019-02-01 01:24:13 +00:00
lovrGpuBindVertexArray ( mesh ) ;
2018-12-27 21:50:42 +00:00
2019-01-04 03:43:35 +00:00
if ( mesh - > indexBuffer & & mesh - > indexCount > 0 ) {
2019-02-01 01:24:13 +00:00
lovrGpuBindBuffer ( BUFFER_INDEX , mesh - > indexBuffer - > id ) ;
2019-05-14 03:35:21 +00:00
lovrBufferUnmap ( mesh - > indexBuffer ) ;
2019-01-27 22:29:06 +00:00
# ifdef LOVR_GL
2019-01-13 18:32:05 +00:00
uint32_t primitiveRestart = mesh - > indexSize = = 4 ? 0xffffffff : 0xffff ;
2019-01-03 09:21:38 +00:00
if ( state . primitiveRestart ! = primitiveRestart ) {
state . primitiveRestart = primitiveRestart ;
glPrimitiveRestartIndex ( primitiveRestart ) ;
}
2019-01-04 17:27:59 +00:00
# endif
2018-12-27 21:50:42 +00:00
}
2019-01-27 22:29:06 +00:00
uint16_t enabledLocations = 0 ;
2019-05-21 03:35:07 +00:00
for ( uint32_t i = 0 ; i < mesh - > attributeCount ; i + + ) {
2019-01-27 22:29:06 +00:00
MeshAttribute * attribute ;
int location ;
2020-05-21 06:24:19 +00:00
bool integer ;
2018-10-25 23:15:41 +00:00
2019-01-27 22:29:06 +00:00
if ( ( attribute = & mesh - > attributes [ i ] ) - > disabled ) { continue ; }
2020-05-21 06:24:19 +00:00
if ( ( location = lovrShaderGetAttributeLocation ( shader , mesh - > attributeNames [ i ] , & integer ) ) < 0 ) { continue ; }
2018-09-04 22:02:55 +00:00
2019-05-14 03:35:21 +00:00
lovrBufferUnmap ( attribute - > buffer ) ;
2019-01-27 22:29:06 +00:00
enabledLocations | = ( 1 < < location ) ;
2018-09-04 22:02:55 +00:00
2019-01-27 22:29:06 +00:00
uint16_t divisor = attribute - > divisor * baseDivisor ;
if ( mesh - > divisors [ location ] ! = divisor ) {
glVertexAttribDivisor ( location , divisor ) ;
mesh - > divisors [ location ] = divisor ;
2019-01-22 20:11:01 +00:00
}
2019-01-27 22:29:06 +00:00
if ( mesh - > locations [ location ] = = i ) { continue ; }
2018-09-04 22:02:55 +00:00
2019-01-27 22:29:06 +00:00
mesh - > locations [ location ] = i ;
lovrGpuBindBuffer ( BUFFER_VERTEX , attribute - > buffer - > id ) ;
GLenum type = convertAttributeType ( attribute - > type ) ;
GLvoid * offset = ( GLvoid * ) ( intptr_t ) attribute - > offset ;
2018-09-04 22:02:55 +00:00
2020-05-21 06:24:19 +00:00
if ( integer ) {
2019-01-27 22:29:06 +00:00
glVertexAttribIPointer ( location , attribute - > components , type , attribute - > stride , offset ) ;
} else {
glVertexAttribPointer ( location , attribute - > components , type , attribute - > normalized , attribute - > stride , offset ) ;
2018-12-25 05:46:50 +00:00
}
2019-01-27 22:29:06 +00:00
}
2018-09-04 22:02:55 +00:00
2019-01-27 22:29:06 +00:00
uint16_t diff = enabledLocations ^ mesh - > enabledLocations ;
if ( diff ! = 0 ) {
2019-05-21 03:35:07 +00:00
for ( uint32_t i = 0 ; i < MAX_ATTRIBUTES ; i + + ) {
2019-01-27 22:29:06 +00:00
if ( diff & ( 1 < < i ) ) {
if ( enabledLocations & ( 1 < < i ) ) {
glEnableVertexAttribArray ( i ) ;
} else {
glDisableVertexAttribArray ( i ) ;
}
2018-12-25 05:46:50 +00:00
}
}
2019-01-27 22:29:06 +00:00
mesh - > enabledLocations = enabledLocations ;
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-12-25 05:46:50 +00:00
static void lovrGpuBindCanvas ( Canvas * canvas , bool willDraw ) {
2019-06-27 22:36:51 +00:00
lovrGpuBindFramebuffer ( canvas - > framebuffer ) ;
if ( canvas - > framebuffer = = 0 ) {
2018-12-25 05:46:50 +00:00
return ;
2018-07-17 06:51:11 +00:00
}
2018-12-25 05:46:50 +00:00
2019-06-27 22:36:51 +00:00
canvas - > needsResolve = willDraw ;
2018-12-25 05:46:50 +00:00
if ( ! canvas - > needsAttach ) {
return ;
2018-08-18 02:52:34 +00:00
}
2018-12-25 05:46:50 +00:00
// We need to synchronize if any of the Canvas attachments have pending writes on them
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2018-12-25 05:46:50 +00:00
Texture * texture = canvas - > attachments [ i ] . texture ;
if ( texture - > incoherent & & ( texture - > incoherent > > BARRIER_CANVAS ) & 1 ) {
lovrGpuSync ( 1 < < BARRIER_CANVAS ) ;
break ;
}
2018-08-12 04:17:22 +00:00
}
2018-12-25 05:46:50 +00:00
# endif
GLenum buffers [ MAX_CANVAS_ATTACHMENTS ] = { GL_NONE } ;
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2019-06-25 08:06:55 +00:00
GLenum drawBuffer = buffers [ i ] = GL_COLOR_ATTACHMENT0 + i ;
2018-12-25 05:46:50 +00:00
Attachment * attachment = & canvas - > attachments [ i ] ;
Texture * texture = attachment - > texture ;
2019-05-20 21:34:03 +00:00
uint32_t slice = attachment - > slice ;
uint32_t level = attachment - > level ;
2018-12-25 05:46:50 +00:00
2019-06-25 08:06:55 +00:00
if ( canvas - > flags . stereo & & state . singlepass = = MULTIVIEW ) {
2019-07-29 00:28:07 +00:00
# ifdef LOVR_WEBGL
lovrThrow ( " Unreachable " ) ;
# else
2020-01-14 23:59:50 +00:00
glFramebufferTextureMultisampleMultiviewOVR ( GL_FRAMEBUFFER , drawBuffer , texture - > id , level , canvas - > flags . msaa , slice , 2 ) ;
2019-07-29 00:28:07 +00:00
# endif
2019-06-25 08:06:55 +00:00
} else {
if ( canvas - > flags . msaa ) {
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , drawBuffer , GL_RENDERBUFFER , texture - > msaaId ) ;
2020-01-14 23:59:50 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , canvas - > resolveBuffer ) ;
2019-06-25 08:06:55 +00:00
}
2018-12-25 05:46:50 +00:00
2019-06-25 08:06:55 +00:00
switch ( texture - > type ) {
case TEXTURE_2D : glFramebufferTexture2D ( GL_READ_FRAMEBUFFER , drawBuffer , GL_TEXTURE_2D , texture - > id , level ) ; break ;
case TEXTURE_CUBE : glFramebufferTexture2D ( GL_READ_FRAMEBUFFER , drawBuffer , GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice , texture - > id , level ) ; break ;
case TEXTURE_ARRAY : glFramebufferTextureLayer ( GL_READ_FRAMEBUFFER , drawBuffer , texture - > id , level , slice ) ; break ;
case TEXTURE_VOLUME : glFramebufferTextureLayer ( GL_READ_FRAMEBUFFER , drawBuffer , texture - > id , level , slice ) ; break ;
}
2018-12-25 05:46:50 +00:00
}
}
glDrawBuffers ( canvas - > attachmentCount , buffers ) ;
GLenum status = glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
switch ( status ) {
case GL_FRAMEBUFFER_COMPLETE : break ;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE : lovrThrow ( " Unable to set Canvas (MSAA settings) " ) ; break ;
case GL_FRAMEBUFFER_UNSUPPORTED : lovrThrow ( " Unable to set Canvas (Texture formats) " ) ; break ;
default : lovrThrow ( " Unable to set Canvas (reason unknown) " ) ; break ;
}
canvas - > needsAttach = false ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-12-25 05:46:50 +00:00
static void lovrGpuBindPipeline ( Pipeline * pipeline ) {
2018-07-18 01:49:42 +00:00
2018-10-25 23:15:41 +00:00
// Alpha Coverage
2018-12-12 06:52:33 +00:00
if ( state . alphaToCoverage ! = pipeline - > alphaSampling ) {
state . alphaToCoverage = pipeline - > alphaSampling ;
if ( state . alphaToCoverage ) {
2018-10-25 23:15:41 +00:00
glEnable ( GL_SAMPLE_ALPHA_TO_COVERAGE ) ;
} else {
glDisable ( GL_SAMPLE_ALPHA_TO_COVERAGE ) ;
}
}
2018-07-17 10:35:01 +00:00
// Blend mode
if ( state . blendMode ! = pipeline - > blendMode | | state . blendAlphaMode ! = pipeline - > blendAlphaMode ) {
state . blendMode = pipeline - > blendMode ;
state . blendAlphaMode = pipeline - > blendAlphaMode ;
2018-07-17 06:51:11 +00:00
2018-12-27 21:50:42 +00:00
if ( state . blendMode = = BLEND_NONE ) {
if ( state . blendEnabled ) {
state . blendEnabled = false ;
glDisable ( GL_BLEND ) ;
}
} else {
if ( ! state . blendEnabled ) {
state . blendEnabled = true ;
glEnable ( GL_BLEND ) ;
}
2018-07-17 06:51:11 +00:00
2018-12-27 21:50:42 +00:00
GLenum srcRGB = state . blendMode = = BLEND_MULTIPLY ? GL_DST_COLOR : GL_ONE ;
if ( srcRGB = = GL_ONE & & state . blendAlphaMode = = BLEND_ALPHA_MULTIPLY ) {
srcRGB = GL_SRC_ALPHA ;
}
2018-07-17 06:51:11 +00:00
2018-12-27 21:50:42 +00:00
switch ( state . blendMode ) {
case BLEND_ALPHA :
glBlendEquation ( GL_FUNC_ADD ) ;
glBlendFuncSeparate ( srcRGB , GL_ONE_MINUS_SRC_ALPHA , GL_ONE , GL_ONE_MINUS_SRC_ALPHA ) ;
break ;
2018-07-17 06:51:11 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_ADD :
glBlendEquation ( GL_FUNC_ADD ) ;
glBlendFuncSeparate ( srcRGB , GL_ONE , GL_ZERO , GL_ONE ) ;
break ;
2018-07-17 10:35:01 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_SUBTRACT :
glBlendEquation ( GL_FUNC_REVERSE_SUBTRACT ) ;
glBlendFuncSeparate ( srcRGB , GL_ONE , GL_ZERO , GL_ONE ) ;
break ;
2018-07-17 10:35:01 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_MULTIPLY :
glBlendEquation ( GL_FUNC_ADD ) ;
glBlendFuncSeparate ( srcRGB , GL_ZERO , GL_DST_COLOR , GL_ZERO ) ;
break ;
2018-07-17 10:35:01 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_LIGHTEN :
glBlendEquation ( GL_MAX ) ;
glBlendFuncSeparate ( srcRGB , GL_ZERO , GL_ONE , GL_ZERO ) ;
break ;
2018-07-17 10:35:01 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_DARKEN :
glBlendEquation ( GL_MIN ) ;
glBlendFuncSeparate ( srcRGB , GL_ZERO , GL_ONE , GL_ZERO ) ;
break ;
2018-07-17 10:35:01 +00:00
2018-12-27 21:50:42 +00:00
case BLEND_SCREEN :
glBlendEquation ( GL_FUNC_ADD ) ;
glBlendFuncSeparate ( srcRGB , GL_ONE_MINUS_SRC_COLOR , GL_ONE , GL_ONE_MINUS_SRC_COLOR ) ;
break ;
case BLEND_NONE : lovrThrow ( " Unreachable " ) ; break ;
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
}
2019-12-10 21:44:51 +00:00
// Color mask
if ( state . colorMask ! = pipeline - > colorMask ) {
state . colorMask = pipeline - > colorMask ;
glColorMask ( state . colorMask & 0x8 , state . colorMask & 0x4 , state . colorMask & 0x2 , state . colorMask & 0x1 ) ;
}
2018-07-17 10:35:01 +00:00
// Culling
if ( state . culling ! = pipeline - > culling ) {
state . culling = pipeline - > culling ;
if ( state . culling ) {
glEnable ( GL_CULL_FACE ) ;
2018-07-17 06:51:11 +00:00
} else {
2018-07-17 10:35:01 +00:00
glDisable ( GL_CULL_FACE ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2020-04-21 22:14:14 +00:00
// Depth test and depth write
bool updateDepthTest = pipeline - > depthTest ! = state . depthTest ;
bool updateDepthWrite = state . depthWrite ! = ( pipeline - > depthWrite & & ! state . stencilWriting ) ;
if ( updateDepthTest | | updateDepthWrite ) {
2021-06-01 18:58:02 +00:00
bool enable = pipeline - > depthTest ! = COMPARE_NONE | | pipeline - > depthWrite ;
2020-04-21 22:14:14 +00:00
if ( enable & & ! state . depthEnabled ) {
glEnable ( GL_DEPTH_TEST ) ;
} else if ( ! enable & & state . depthEnabled ) {
2018-07-17 10:35:01 +00:00
glDisable ( GL_DEPTH_TEST ) ;
2018-07-17 06:51:11 +00:00
}
2021-06-01 18:58:02 +00:00
2020-04-21 22:14:14 +00:00
state . depthEnabled = enable ;
2021-06-01 18:58:02 +00:00
state . depthTest = pipeline - > depthTest ;
2018-07-17 06:51:11 +00:00
2020-04-21 22:14:14 +00:00
if ( enable & & updateDepthTest ) {
glDepthFunc ( convertCompareMode ( state . depthTest ) ) ;
}
if ( enable & & updateDepthWrite ) {
state . depthWrite = pipeline - > depthWrite & & ! state . stencilWriting ;
glDepthMask ( state . depthWrite ) ;
}
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
// Line width
if ( state . lineWidth ! = pipeline - > lineWidth ) {
2018-07-17 21:53:21 +00:00
state . lineWidth = pipeline - > lineWidth ;
2018-07-17 10:35:01 +00:00
glLineWidth ( state . lineWidth ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
// Stencil mode
if ( ! state . stencilWriting & & ( state . stencilMode ! = pipeline - > stencilMode | | state . stencilValue ! = pipeline - > stencilValue ) ) {
state . stencilMode = pipeline - > stencilMode ;
state . stencilValue = pipeline - > stencilValue ;
if ( state . stencilMode ! = COMPARE_NONE ) {
if ( ! state . stencilEnabled ) {
state . stencilEnabled = true ;
glEnable ( GL_STENCIL_TEST ) ;
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
GLenum glMode = GL_ALWAYS ;
switch ( state . stencilMode ) {
case COMPARE_EQUAL : glMode = GL_EQUAL ; break ;
case COMPARE_NEQUAL : glMode = GL_NOTEQUAL ; break ;
case COMPARE_LESS : glMode = GL_GREATER ; break ;
case COMPARE_LEQUAL : glMode = GL_GEQUAL ; break ;
case COMPARE_GREATER : glMode = GL_LESS ; break ;
case COMPARE_GEQUAL : glMode = GL_LEQUAL ; break ;
default : break ;
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
glStencilFunc ( glMode , state . stencilValue , 0xff ) ;
glStencilOp ( GL_KEEP , GL_KEEP , GL_KEEP ) ;
} else if ( state . stencilEnabled ) {
state . stencilEnabled = false ;
glDisable ( GL_STENCIL_TEST ) ;
}
2018-07-17 06:51:11 +00:00
}
2018-12-25 05:46:50 +00:00
// Winding
if ( state . winding ! = pipeline - > winding ) {
state . winding = pipeline - > winding ;
glFrontFace ( state . winding = = WINDING_CLOCKWISE ? GL_CW : GL_CCW ) ;
}
// Wireframe
2019-01-29 23:05:23 +00:00
# ifdef LOVR_GL
2018-12-25 05:46:50 +00:00
if ( state . wireframe ! = pipeline - > wireframe ) {
state . wireframe = pipeline - > wireframe ;
glPolygonMode ( GL_FRONT_AND_BACK , state . wireframe ? GL_LINE : GL_FILL ) ;
}
# endif
}
static void lovrGpuBindShader ( Shader * shader ) {
lovrGpuUseProgram ( shader - > program ) ;
// Figure out if we need to wait for pending writes on resources to complete
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-12-25 05:46:50 +00:00
uint8_t flags = 0 ;
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > blocks [ BLOCK_COMPUTE ] . length ; i + + ) {
UniformBlock * block = & shader - > blocks [ BLOCK_COMPUTE ] . data [ i ] ;
2018-12-25 05:46:50 +00:00
if ( block - > source & & ( block - > source - > incoherent > > BARRIER_BLOCK ) & 1 ) {
flags | = 1 < < BARRIER_BLOCK ;
break ;
}
}
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > uniforms . length ; i + + ) {
Uniform * uniform = & shader - > uniforms . data [ i ] ;
2018-12-25 05:46:50 +00:00
if ( uniform - > type = = UNIFORM_SAMPLER ) {
2020-01-23 19:18:04 +00:00
for ( int j = 0 ; j < uniform - > count ; j + + ) {
Texture * texture = uniform - > value . textures [ j ] ;
2018-12-25 05:46:50 +00:00
if ( texture & & texture - > incoherent & & ( texture - > incoherent > > BARRIER_UNIFORM_TEXTURE ) & 1 ) {
flags | = 1 < < BARRIER_UNIFORM_TEXTURE ;
if ( flags & ( 1 < < BARRIER_UNIFORM_IMAGE ) ) {
break ;
}
}
}
} else if ( uniform - > type = = UNIFORM_IMAGE ) {
2020-01-23 19:18:04 +00:00
for ( int j = 0 ; j < uniform - > count ; j + + ) {
Texture * texture = uniform - > value . images [ j ] . texture ;
2018-12-25 05:46:50 +00:00
if ( texture & & texture - > incoherent & & ( texture - > incoherent > > BARRIER_UNIFORM_IMAGE ) & 1 ) {
flags | = 1 < < BARRIER_UNIFORM_IMAGE ;
if ( flags & ( 1 < < BARRIER_UNIFORM_TEXTURE ) ) {
break ;
}
}
}
}
}
lovrGpuSync ( flags ) ;
# endif
// Bind uniforms
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > uniforms . length ; i + + ) {
Uniform * uniform = & shader - > uniforms . data [ i ] ;
2018-12-25 05:46:50 +00:00
if ( uniform - > type ! = UNIFORM_SAMPLER & & uniform - > type ! = UNIFORM_IMAGE & & ! uniform - > dirty ) {
continue ;
}
uniform - > dirty = false ;
int count = uniform - > count ;
void * data = uniform - > value . data ;
switch ( uniform - > type ) {
case UNIFORM_FLOAT :
switch ( uniform - > components ) {
case 1 : glUniform1fv ( uniform - > location , count , data ) ; break ;
case 2 : glUniform2fv ( uniform - > location , count , data ) ; break ;
case 3 : glUniform3fv ( uniform - > location , count , data ) ; break ;
case 4 : glUniform4fv ( uniform - > location , count , data ) ; break ;
}
break ;
case UNIFORM_INT :
switch ( uniform - > components ) {
case 1 : glUniform1iv ( uniform - > location , count , data ) ; break ;
case 2 : glUniform2iv ( uniform - > location , count , data ) ; break ;
case 3 : glUniform3iv ( uniform - > location , count , data ) ; break ;
case 4 : glUniform4iv ( uniform - > location , count , data ) ; break ;
}
break ;
case UNIFORM_MATRIX :
switch ( uniform - > components ) {
case 2 : glUniformMatrix2fv ( uniform - > location , count , GL_FALSE , data ) ; break ;
case 3 : glUniformMatrix3fv ( uniform - > location , count , GL_FALSE , data ) ; break ;
case 4 : glUniformMatrix4fv ( uniform - > location , count , GL_FALSE , data ) ; break ;
}
break ;
case UNIFORM_IMAGE :
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2020-01-23 19:18:04 +00:00
for ( int j = 0 ; j < count ; j + + ) {
2021-02-09 03:17:47 +00:00
StorageImage * image = & uniform - > value . images [ j ] ;
2018-12-25 05:46:50 +00:00
Texture * texture = image - > texture ;
2019-08-29 21:26:44 +00:00
lovrAssert ( ! texture | | texture - > type = = uniform - > textureType , " Uniform texture type mismatch for uniform '%s' " , uniform - > name ) ;
2018-12-25 05:46:50 +00:00
// If the Shader can write to the texture, mark it as incoherent
if ( texture & & image - > access ! = ACCESS_READ ) {
for ( Barrier barrier = BARRIER_BLOCK + 1 ; barrier < MAX_BARRIERS ; barrier + + ) {
texture - > incoherent | = 1 < < barrier ;
2019-06-08 10:45:03 +00:00
arr_push ( & state . incoherents [ barrier ] , texture ) ;
2018-12-25 05:46:50 +00:00
}
}
2020-05-18 21:14:17 +00:00
lovrGpuBindImage ( image , uniform - > baseSlot + j , uniform - > name ) ;
2018-12-25 05:46:50 +00:00
}
# endif
break ;
case UNIFORM_SAMPLER :
2020-01-23 19:18:04 +00:00
for ( int j = 0 ; j < count ; j + + ) {
Texture * texture = uniform - > value . textures [ j ] ;
2019-08-29 21:26:44 +00:00
lovrAssert ( ! texture | | texture - > type = = uniform - > textureType , " Uniform texture type mismatch for uniform '%s' " , uniform - > name ) ;
lovrAssert ( ! texture | | ( uniform - > shadow = = ( texture - > compareMode ! = COMPARE_NONE ) ) , " Uniform '%s' requires a Texture with%s a compare mode " , uniform - > name , uniform - > shadow ? " " : " out " ) ;
2020-01-23 19:18:04 +00:00
lovrGpuBindTexture ( texture , uniform - > baseSlot + j ) ;
2018-12-25 05:46:50 +00:00
}
break ;
}
}
// Bind uniform blocks
2019-01-16 16:52:21 +00:00
for ( BlockType type = BLOCK_UNIFORM ; type < = BLOCK_COMPUTE ; type + + ) {
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > blocks [ type ] . length ; i + + ) {
UniformBlock * block = & shader - > blocks [ type ] . data [ i ] ;
2018-12-25 05:46:50 +00:00
if ( block - > source ) {
2019-01-16 16:52:21 +00:00
if ( type = = BLOCK_COMPUTE & & block - > access ! = ACCESS_READ ) {
2018-12-27 21:50:42 +00:00
block - > source - > incoherent | = ( 1 < < BARRIER_BLOCK ) ;
2019-06-08 10:45:03 +00:00
arr_push ( & state . incoherents [ BARRIER_BLOCK ] , block - > source ) ;
2018-12-27 21:50:42 +00:00
}
2018-07-17 06:51:11 +00:00
2019-05-14 03:35:21 +00:00
lovrBufferUnmap ( block - > source ) ;
2018-12-27 21:50:42 +00:00
lovrGpuBindBlockBuffer ( type , block - > source - > id , block - > slot , block - > offset , block - > size ) ;
2018-12-25 05:46:50 +00:00
} else {
2018-12-27 21:50:42 +00:00
lovrGpuBindBlockBuffer ( type , 0 , block - > slot , 0 , 0 ) ;
2018-12-25 05:46:50 +00:00
}
}
2018-07-17 06:51:11 +00:00
}
2018-08-31 13:03:35 +00:00
}
2018-07-17 06:51:11 +00:00
2019-01-04 09:40:21 +00:00
static void lovrGpuSetViewports ( float * viewport , uint32_t count ) {
2019-01-04 04:07:45 +00:00
if ( state . viewportCount ! = count | | memcmp ( state . viewports , viewport , count * 4 * sizeof ( float ) ) ) {
memcpy ( state . viewports , viewport , count * 4 * sizeof ( float ) ) ;
state . viewportCount = count ;
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2019-01-04 04:07:45 +00:00
if ( count > 1 ) {
2018-08-31 13:03:35 +00:00
glViewportArrayv ( 0 , count , viewport ) ;
2019-01-04 04:07:45 +00:00
} else {
2018-08-31 13:03:35 +00:00
# endif
glViewport ( viewport [ 0 ] , viewport [ 1 ] , viewport [ 2 ] , viewport [ 3 ] ) ;
}
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-07-17 06:51:11 +00:00
}
2018-08-31 13:03:35 +00:00
# endif
}
2018-07-17 06:51:11 +00:00
2018-12-25 05:46:50 +00:00
// GPU
2021-02-19 16:05:57 +00:00
# ifndef LOVR_WEBGL
2020-07-29 20:56:46 +00:00
static void GLAPIENTRY onMessage ( GLenum source , GLenum type , GLuint id , GLenum severity , GLsizei length , const GLchar * message , const void * userdata ) {
2020-07-28 22:12:30 +00:00
int level ;
switch ( severity ) {
case GL_DEBUG_SEVERITY_HIGH : level = LOG_ERROR ; break ;
case GL_DEBUG_SEVERITY_MEDIUM : level = LOG_WARN ; break ;
case GL_DEBUG_SEVERITY_LOW : level = LOG_INFO ; break ;
default : level = LOG_DEBUG ; break ;
}
lovrLog ( level , " GL " , message ) ;
}
2021-02-19 16:05:57 +00:00
# endif
2020-07-28 22:12:30 +00:00
2021-02-20 06:10:24 +00:00
void lovrGpuInit ( void ( * getProcAddress ( const char * ) ) ( void ) , bool debug ) {
2019-01-30 04:57:02 +00:00
# ifdef LOVR_GL
2018-12-25 05:46:50 +00:00
gladLoadGLLoader ( ( GLADloadproc ) getProcAddress ) ;
2019-01-30 04:57:02 +00:00
# elif defined(LOVR_GLES)
gladLoadGLES2Loader ( ( GLADloadproc ) getProcAddress ) ;
# endif
2020-08-08 23:37:58 +00:00
# ifndef LOVR_WEBGL
2020-09-17 03:40:39 +00:00
if ( debug & & ( GLAD_GL_KHR_debug | | GLAD_GL_ES_VERSION_3_2 ) ) {
2020-07-28 22:12:30 +00:00
glEnable ( GL_DEBUG_OUTPUT_SYNCHRONOUS ) ;
glDebugMessageCallback ( onMessage , NULL ) ;
}
2021-04-13 16:52:28 +00:00
const char * vendor = ( const char * ) glGetString ( GL_VENDOR ) ;
state . amd = vendor & & ( strstr ( vendor , " ATI Technologies " ) | | strstr ( vendor , " AMD " ) | | strstr ( vendor , " Advanced Micro Devices " ) ) ;
2019-06-10 06:59:57 +00:00
state . features . astc = GLAD_GL_ES_VERSION_3_2 ;
2021-02-19 21:07:27 +00:00
state . features . compute = GLAD_GL_ES_VERSION_3_1 | | ( GLAD_GL_ARB_compute_shader & & GLAD_GL_ARB_shader_storage_buffer_object & & GLAD_GL_ARB_shader_image_load_store ) ;
2019-06-10 06:59:57 +00:00
state . features . dxt = GLAD_GL_EXT_texture_compression_s3tc ;
2019-06-25 08:06:55 +00:00
state . features . instancedStereo = GLAD_GL_ARB_viewport_array & & GLAD_GL_AMD_vertex_shader_viewport_index & & GLAD_GL_ARB_fragment_layer_viewport ;
2020-01-31 01:48:01 +00:00
state . features . multiview = GLAD_GL_ES_VERSION_3_0 & & GLAD_GL_OVR_multiview2 & & GLAD_GL_OVR_multiview_multisampled_render_to_texture ;
2020-09-25 02:04:50 +00:00
state . features . timers = GLAD_GL_VERSION_3_3 ;
2020-02-16 09:23:27 +00:00
# ifdef LOVR_GL
2018-12-25 05:46:50 +00:00
glEnable ( GL_LINE_SMOOTH ) ;
glEnable ( GL_PROGRAM_POINT_SIZE ) ;
2019-05-04 00:53:33 +00:00
glEnable ( GL_FRAMEBUFFER_SRGB ) ;
2019-08-10 00:22:00 +00:00
glEnable ( GL_TEXTURE_CUBE_MAP_SEAMLESS ) ;
# endif
2018-12-25 05:46:50 +00:00
glGetFloatv ( GL_POINT_SIZE_RANGE , state . limits . pointSizes ) ;
2019-06-27 04:47:22 +00:00
2020-09-18 02:42:54 +00:00
if ( state . features . compute ) {
glGetIntegeri_v ( GL_MAX_COMPUTE_WORK_GROUP_COUNT , 0 , & state . limits . compute [ 0 ] ) ;
glGetIntegeri_v ( GL_MAX_COMPUTE_WORK_GROUP_COUNT , 1 , & state . limits . compute [ 1 ] ) ;
glGetIntegeri_v ( GL_MAX_COMPUTE_WORK_GROUP_COUNT , 2 , & state . limits . compute [ 2 ] ) ;
}
2020-01-31 01:48:01 +00:00
if ( state . features . multiview ) {
2019-06-27 04:47:22 +00:00
state . singlepass = MULTIVIEW ;
} else if ( state . features . instancedStereo ) {
state . singlepass = INSTANCED_STEREO ;
} else {
state . singlepass = NONE ;
}
2018-12-25 05:46:50 +00:00
# else
glGetFloatv ( GL_ALIASED_POINT_SIZE_RANGE , state . limits . pointSizes ) ;
# endif
2019-01-29 23:05:23 +00:00
2018-12-25 05:46:50 +00:00
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & state . limits . textureSize ) ;
glGetIntegerv ( GL_MAX_SAMPLES , & state . limits . textureMSAA ) ;
glGetIntegerv ( GL_MAX_UNIFORM_BLOCK_SIZE , & state . limits . blockSize ) ;
2018-12-27 21:50:42 +00:00
glGetIntegerv ( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT , & state . limits . blockAlign ) ;
2018-12-25 05:46:50 +00:00
glGetFloatv ( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , & state . limits . textureAnisotropy ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
2019-01-29 23:05:23 +00:00
# ifdef LOVR_GLES
glEnable ( GL_PRIMITIVE_RESTART_FIXED_INDEX ) ;
# elif defined(LOVR_GL)
glEnable ( GL_PRIMITIVE_RESTART ) ;
state . primitiveRestart = 0xffffffff ;
glPrimitiveRestartIndex ( state . primitiveRestart ) ;
# endif
2019-10-15 18:34:49 +00:00
state . activeTexture = 0 ;
glActiveTexture ( GL_TEXTURE0 + state . activeTexture ) ;
2018-12-25 05:46:50 +00:00
state . alphaToCoverage = false ;
glDisable ( GL_SAMPLE_ALPHA_TO_COVERAGE ) ;
2018-12-27 21:50:42 +00:00
state . blendEnabled = true ;
2018-12-25 05:46:50 +00:00
state . blendMode = BLEND_ALPHA ;
state . blendAlphaMode = BLEND_ALPHA_MULTIPLY ;
2018-12-27 21:50:42 +00:00
glEnable ( GL_BLEND ) ;
2018-12-25 05:46:50 +00:00
glBlendEquation ( GL_FUNC_ADD ) ;
glBlendFuncSeparate ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA , GL_ONE , GL_ONE_MINUS_SRC_ALPHA ) ;
2019-12-10 21:44:51 +00:00
state . colorMask = 0xf ;
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2018-12-25 05:46:50 +00:00
state . culling = false ;
glDisable ( GL_CULL_FACE ) ;
state . depthEnabled = true ;
state . depthTest = COMPARE_LEQUAL ;
state . depthWrite = true ;
glEnable ( GL_DEPTH_TEST ) ;
glDepthFunc ( convertCompareMode ( state . depthTest ) ) ;
glDepthMask ( state . depthWrite ) ;
2019-12-10 21:44:51 +00:00
state . lineWidth = 1.f ;
2018-12-25 05:46:50 +00:00
glLineWidth ( state . lineWidth ) ;
state . stencilEnabled = false ;
state . stencilMode = COMPARE_NONE ;
state . stencilValue = 0 ;
state . stencilWriting = false ;
glDisable ( GL_STENCIL_TEST ) ;
state . winding = WINDING_COUNTERCLOCKWISE ;
glFrontFace ( GL_CCW ) ;
state . wireframe = false ;
2019-01-29 23:05:23 +00:00
# ifdef LOVR_GL
2018-12-25 05:46:50 +00:00
glPolygonMode ( GL_FRONT_AND_BACK , GL_FILL ) ;
# endif
for ( int i = 0 ; i < MAX_BARRIERS ; i + + ) {
2022-03-14 19:27:58 +00:00
arr_init ( & state . incoherents [ i ] , arr_alloc ) ;
2018-12-25 05:46:50 +00:00
}
2019-06-20 18:35:46 +00:00
2021-02-09 03:17:47 +00:00
Image * image = lovrImageCreate ( 1 , 1 , NULL , 0xff , FORMAT_RGBA ) ;
state . defaultTexture = lovrTextureCreate ( TEXTURE_2D , & image , 1 , true , false , 0 ) ;
2019-06-20 18:35:46 +00:00
lovrTextureSetFilter ( state . defaultTexture , ( TextureFilter ) { . mode = FILTER_NEAREST } ) ;
lovrTextureSetWrap ( state . defaultTexture , ( TextureWrap ) { WRAP_CLAMP , WRAP_CLAMP , WRAP_CLAMP } ) ;
2021-02-09 03:17:47 +00:00
lovrRelease ( image , lovrImageDestroy ) ;
2019-09-07 22:07:07 +00:00
map_init ( & state . timerMap , 4 ) ;
state . queryPool . next = ~ 0u ;
state . activeTimer = ~ 0u ;
2018-12-25 05:46:50 +00:00
}
void lovrGpuDestroy ( ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( state . defaultTexture , lovrTextureDestroy ) ;
2018-12-25 05:46:50 +00:00
for ( int i = 0 ; i < MAX_TEXTURES ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( state . textures [ i ] , lovrTextureDestroy ) ;
2018-12-25 05:46:50 +00:00
}
for ( int i = 0 ; i < MAX_IMAGES ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( state . images [ i ] . texture , lovrTextureDestroy ) ;
2018-12-25 05:46:50 +00:00
}
for ( int i = 0 ; i < MAX_BARRIERS ; i + + ) {
2019-06-08 10:45:03 +00:00
arr_free ( & state . incoherents [ i ] ) ;
2018-12-25 05:46:50 +00:00
}
2019-09-07 22:07:07 +00:00
glDeleteQueries ( state . queryPool . count , state . queryPool . queries ) ;
free ( state . queryPool . queries ) ;
arr_free ( & state . timers ) ;
map_free ( & state . timerMap ) ;
2018-12-25 05:46:50 +00:00
memset ( & state , 0 , sizeof ( state ) ) ;
}
2018-08-31 13:03:35 +00:00
void lovrGpuClear ( Canvas * canvas , Color * color , float * depth , int * stencil ) {
2018-12-25 05:46:50 +00:00
lovrGpuBindCanvas ( canvas , true ) ;
2018-07-17 06:51:11 +00:00
2018-08-31 13:03:35 +00:00
if ( color ) {
2018-10-24 15:19:46 +00:00
int count = canvas ? canvas - > attachmentCount : 1 ;
2018-08-31 13:03:35 +00:00
for ( int i = 0 ; i < count ; i + + ) {
glClearBufferfv ( GL_COLOR , i , ( float [ ] ) { color - > r , color - > g , color - > b , color - > a } ) ;
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-08-31 13:03:35 +00:00
if ( depth & & ! state . depthWrite ) {
state . depthWrite = true ;
glDepthMask ( state . depthWrite ) ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-08-31 13:03:35 +00:00
if ( depth & & stencil ) {
glClearBufferfi ( GL_DEPTH_STENCIL , 0 , * depth , * stencil ) ;
} else if ( depth ) {
glClearBufferfv ( GL_DEPTH , 0 , depth ) ;
} else if ( stencil ) {
glClearBufferiv ( GL_STENCIL , 0 , stencil ) ;
2018-07-17 10:35:01 +00:00
}
2018-08-31 13:03:35 +00:00
}
2018-07-17 06:51:11 +00:00
2018-12-25 05:46:50 +00:00
void lovrGpuCompute ( Shader * shader , int x , int y , int z ) {
2019-01-29 23:05:23 +00:00
# ifdef LOVR_WEBGL
2018-12-25 05:46:50 +00:00
lovrThrow ( " Compute shaders are not supported on this system " ) ;
# else
2020-01-30 02:22:16 +00:00
lovrAssert ( state . features . compute , " Compute shaders are not supported on this system " ) ;
2018-12-25 05:46:50 +00:00
lovrAssert ( shader - > type = = SHADER_COMPUTE , " Attempt to use a non-compute shader for a compute operation " ) ;
2020-09-18 02:42:54 +00:00
lovrAssert ( x < = state . limits . compute [ 0 ] , " Compute x size %d exceeds the maximum of %d " , state . limits . compute [ 0 ] ) ;
lovrAssert ( y < = state . limits . compute [ 1 ] , " Compute y size %d exceeds the maximum of %d " , state . limits . compute [ 1 ] ) ;
lovrAssert ( z < = state . limits . compute [ 2 ] , " Compute z size %d exceeds the maximum of %d " , state . limits . compute [ 2 ] ) ;
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2018-12-25 05:46:50 +00:00
lovrGpuBindShader ( shader ) ;
glDispatchCompute ( x , y , z ) ;
# endif
}
2018-10-26 16:14:57 +00:00
void lovrGpuDiscard ( Canvas * canvas , bool color , bool depth , bool stencil ) {
2019-01-29 23:05:23 +00:00
# ifndef LOVR_GL
2018-12-25 05:46:50 +00:00
lovrGpuBindCanvas ( canvas , false ) ;
2018-10-26 16:14:57 +00:00
GLenum attachments [ MAX_CANVAS_ATTACHMENTS + 1 ] = { 0 } ;
int count = 0 ;
if ( color ) {
2019-06-27 22:36:51 +00:00
int n = MAX ( canvas - > attachmentCount , 1 ) ;
2018-10-26 16:14:57 +00:00
for ( int i = 0 ; i < n ; i + + ) {
attachments [ count + + ] = GL_COLOR_ATTACHMENT0 + i ;
}
}
if ( depth ) {
attachments [ count + + ] = GL_DEPTH_ATTACHMENT ;
}
if ( stencil ) {
attachments [ count + + ] = GL_STENCIL_ATTACHMENT ;
}
glInvalidateFramebuffer ( GL_FRAMEBUFFER , count , attachments ) ;
# endif
}
2018-12-27 21:50:42 +00:00
void lovrGpuDraw ( DrawCommand * draw ) {
2019-06-25 08:06:55 +00:00
lovrAssert ( state . singlepass ! = MULTIVIEW | | draw - > shader - > multiview = = draw - > canvas - > flags . stereo , " Shader and Canvas multiview settings must match! " ) ;
2019-06-27 22:36:51 +00:00
uint32_t viewportCount = ( draw - > canvas - > flags . stereo & & state . singlepass ! = MULTIVIEW ) ? 2 : 1 ;
2019-06-25 08:06:55 +00:00
uint32_t drawCount = state . singlepass = = NONE ? viewportCount : 1 ;
uint32_t instanceMultiplier = state . singlepass = = INSTANCED_STEREO ? viewportCount : 1 ;
uint32_t viewportsPerDraw = instanceMultiplier ;
uint32_t instances = MAX ( draw - > instances , 1 ) * instanceMultiplier ;
2019-06-27 22:36:51 +00:00
float w = state . singlepass = = MULTIVIEW ? draw - > canvas - > width : draw - > canvas - > width / ( float ) viewportCount ;
float h = draw - > canvas - > height ;
2019-06-25 08:06:55 +00:00
float viewports [ 2 ] [ 4 ] = { { 0.f , 0.f , w , h } , { w , 0.f , w , h } } ;
lovrShaderSetInts ( draw - > shader , " lovrViewportCount " , & ( int ) { viewportCount } , 0 , 1 ) ;
2018-12-27 21:50:42 +00:00
lovrGpuBindCanvas ( draw - > canvas , true ) ;
lovrGpuBindPipeline ( & draw - > pipeline ) ;
2019-06-25 08:06:55 +00:00
lovrGpuBindMesh ( draw - > mesh , draw - > shader , instanceMultiplier ) ;
2018-12-27 21:50:42 +00:00
2019-01-04 09:40:21 +00:00
for ( uint32_t i = 0 ; i < drawCount ; i + + ) {
2019-06-25 08:06:55 +00:00
lovrGpuSetViewports ( & viewports [ i ] [ 0 ] , viewportsPerDraw ) ;
lovrShaderSetInts ( draw - > shader , " lovrViewID " , & ( int ) { i } , 0 , 1 ) ;
2018-12-27 21:50:42 +00:00
lovrGpuBindShader ( draw - > shader ) ;
Mesh * mesh = draw - > mesh ;
2019-06-28 03:58:42 +00:00
GLenum topology = convertTopology ( draw - > topology ) ;
2018-12-27 21:50:42 +00:00
if ( mesh - > indexCount > 0 ) {
GLenum indexType = mesh - > indexSize = = sizeof ( uint16_t ) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT ;
2019-01-17 22:14:13 +00:00
GLvoid * offset = ( GLvoid * ) ( mesh - > indexOffset + draw - > rangeStart * mesh - > indexSize ) ;
2018-12-27 21:50:42 +00:00
if ( instances > 1 ) {
2019-06-28 03:58:42 +00:00
glDrawElementsInstanced ( topology , draw - > rangeCount , indexType , offset , instances ) ;
2018-12-25 05:11:34 +00:00
} else {
2019-06-28 03:58:42 +00:00
glDrawElements ( topology , draw - > rangeCount , indexType , offset ) ;
2018-12-27 21:50:42 +00:00
}
} else {
if ( instances > 1 ) {
2019-06-28 03:58:42 +00:00
glDrawArraysInstanced ( topology , draw - > rangeStart , draw - > rangeCount , instances ) ;
2018-12-27 21:50:42 +00:00
} else {
2019-06-28 03:58:42 +00:00
glDrawArrays ( topology , draw - > rangeStart , draw - > rangeCount ) ;
2018-12-25 05:11:34 +00:00
}
2018-12-25 04:26:47 +00:00
}
2018-12-27 21:50:42 +00:00
state . stats . drawCalls + + ;
2018-12-25 04:26:47 +00:00
}
}
2018-12-25 05:46:50 +00:00
void lovrGpuPresent ( ) {
2020-01-31 11:35:42 +00:00
state . stats . shaderSwitches = 0 ;
state . stats . renderPasses = 0 ;
state . stats . drawCalls = 0 ;
2018-12-25 05:46:50 +00:00
}
void lovrGpuStencil ( StencilAction action , int replaceValue , StencilCallback callback , void * userdata ) {
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2021-11-23 22:16:57 +00:00
uint8_t lastColorMask = state . colorMask ;
state . colorMask = 0 ;
glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE ) ;
2018-12-25 05:46:50 +00:00
if ( ! state . stencilEnabled ) {
state . stencilEnabled = true ;
glEnable ( GL_STENCIL_TEST ) ;
}
GLenum glAction ;
switch ( action ) {
case STENCIL_REPLACE : glAction = GL_REPLACE ; break ;
case STENCIL_INCREMENT : glAction = GL_INCR ; break ;
case STENCIL_DECREMENT : glAction = GL_DECR ; break ;
case STENCIL_INCREMENT_WRAP : glAction = GL_INCR_WRAP ; break ;
case STENCIL_DECREMENT_WRAP : glAction = GL_DECR_WRAP ; break ;
case STENCIL_INVERT : glAction = GL_INVERT ; break ;
2019-04-21 01:42:25 +00:00
default : lovrThrow ( " Unreachable " ) ;
2018-12-25 05:46:50 +00:00
}
glStencilFunc ( GL_ALWAYS , replaceValue , 0xff ) ;
glStencilOp ( GL_KEEP , GL_KEEP , glAction ) ;
state . stencilWriting = true ;
callback ( userdata ) ;
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2018-12-25 05:46:50 +00:00
state . stencilWriting = false ;
state . stencilMode = ~ 0 ; // Dirty
2021-11-23 22:16:57 +00:00
state . colorMask = lastColorMask ;
glColorMask ( state . colorMask & 0x8 , state . colorMask & 0x4 , state . colorMask & 0x2 , state . colorMask & 0x1 ) ;
2018-12-25 05:46:50 +00:00
}
2018-09-01 06:24:59 +00:00
void lovrGpuDirtyTexture ( ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( state . textures [ state . activeTexture ] , lovrTextureDestroy ) ;
2018-09-01 06:24:59 +00:00
state . textures [ state . activeTexture ] = NULL ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2019-05-26 11:37:00 +00:00
void lovrGpuTick ( const char * label ) {
2020-09-20 00:25:54 +00:00
# ifdef LOVR_GL
2019-09-07 22:07:07 +00:00
lovrAssert ( state . activeTimer = = ~ 0u , " Attempt to start a new GPU timer while one is already active! " ) ;
QueryPool * pool = & state . queryPool ;
uint64_t hash = hash64 ( label , strlen ( label ) ) ;
uint64_t index = map_get ( & state . timerMap , hash ) ;
// Create new timer
if ( index = = MAP_NIL ) {
index = state . timers . length + + ;
map_set ( & state . timerMap , hash , index ) ;
arr_reserve ( & state . timers , state . timers . length ) ;
state . timers . data [ index ] . head = ~ 0u ;
state . timers . data [ index ] . tail = ~ 0u ;
}
Timer * timer = & state . timers . data [ index ] ;
state . activeTimer = index ;
// Expand pool if no unused timers are available.
// The pool manages one memory allocation split into two chunks.
// - The first chunk contains OpenGL query objects (GLuint).
// - The second chunk is a linked list of query indices (uint32_t), used for two purposes:
// - For inactive queries, pool->chain[query] points to the next inactive query (freelist).
// - For active queries, pool->chain[query] points to the next active query for that timer.
// When resizing the query pool allocation, the second half of the old allocation needs to be
// copied to the second half of the new allocation.
if ( pool - > next = = ~ 0u ) {
uint32_t n = pool - > count ;
pool - > count = n = = 0 ? 4 : ( n < < 1 ) ;
pool - > queries = realloc ( pool - > queries , pool - > count * ( sizeof ( GLuint ) + sizeof ( uint32_t ) ) ) ;
lovrAssert ( pool - > queries , " Out of memory " ) ;
pool - > chain = pool - > queries + pool - > count ;
memcpy ( pool - > chain , pool - > queries + n , n * sizeof ( uint32_t ) ) ;
glGenQueries ( n ? n : pool - > count , pool - > queries + n ) ;
for ( uint32_t i = n ; i < pool - > count - 1 ; i + + ) {
pool - > chain [ i ] = i + 1 ;
}
pool - > chain [ pool - > count - 1 ] = ~ 0u ;
pool - > next = n ;
}
// Start query, update linked list pointers
uint32_t query = pool - > next ;
glBeginQuery ( GL_TIME_ELAPSED , pool - > queries [ query ] ) ;
if ( timer - > tail ! = ~ 0u ) { pool - > chain [ timer - > tail ] = query ; }
if ( timer - > head = = ~ 0u ) { timer - > head = query ; }
pool - > next = pool - > chain [ query ] ;
pool - > chain [ query ] = ~ 0u ;
timer - > tail = query ;
2019-07-29 00:28:07 +00:00
# endif
2019-05-26 11:37:00 +00:00
}
2019-09-19 01:21:38 +00:00
double lovrGpuTock ( const char * label ) {
2020-09-20 00:25:54 +00:00
# ifdef LOVR_GL
2019-09-07 22:07:07 +00:00
QueryPool * pool = & state . queryPool ;
uint64_t hash = hash64 ( label , strlen ( label ) ) ;
uint64_t index = map_get ( & state . timerMap , hash ) ;
if ( index = = MAP_NIL ) {
return 0. ;
}
Timer * timer = & state . timers . data [ index ] ;
if ( state . activeTimer ! = index ) {
return timer - > nanoseconds / 1e9 ;
}
2019-05-26 11:37:00 +00:00
glEndQuery ( GL_TIME_ELAPSED ) ;
2019-09-07 22:07:07 +00:00
state . activeTimer = ~ 0u ;
// Repeatedly check timer's oldest pending query for completion
for ( ; ; ) {
int query = timer - > head ;
2019-05-26 11:37:00 +00:00
GLuint available ;
2019-09-07 22:07:07 +00:00
glGetQueryObjectuiv ( pool - > queries [ query ] , GL_QUERY_RESULT_AVAILABLE , & available ) ;
2019-05-26 11:37:00 +00:00
if ( ! available ) {
break ;
}
2019-09-07 22:07:07 +00:00
// Update timer result
glGetQueryObjectui64v ( pool - > queries [ query ] , GL_QUERY_RESULT , & timer - > nanoseconds ) ;
// Update timer's head pointer and return the completed query back to the pool
timer - > head = pool - > chain [ query ] ;
pool - > chain [ query ] = pool - > next ;
pool - > next = query ;
if ( timer - > head = = ~ 0u ) {
timer - > tail = ~ 0u ;
break ;
}
2019-05-26 11:37:00 +00:00
}
2019-09-19 01:21:38 +00:00
2019-09-07 22:07:07 +00:00
return timer - > nanoseconds / 1e9 ;
2019-07-29 00:28:07 +00:00
# endif
2019-10-08 02:50:17 +00:00
return 0. ;
2019-05-26 11:37:00 +00:00
}
2019-03-15 04:24:25 +00:00
const GpuFeatures * lovrGpuGetFeatures ( ) {
2018-08-31 13:03:35 +00:00
return & state . features ;
}
2018-07-17 06:51:11 +00:00
2018-08-31 13:03:35 +00:00
const GpuLimits * lovrGpuGetLimits ( ) {
return & state . limits ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-08-31 13:03:35 +00:00
const GpuStats * lovrGpuGetStats ( ) {
return & state . stats ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
// Texture
2018-07-17 06:51:11 +00:00
2021-02-09 03:17:47 +00:00
Texture * lovrTextureCreate ( TextureType type , Image * * slices , uint32_t sliceCount , bool srgb , bool mipmaps , uint32_t msaa ) {
2021-02-09 00:23:18 +00:00
Texture * texture = calloc ( 1 , sizeof ( Texture ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
2018-08-09 23:12:57 +00:00
texture - > type = type ;
texture - > srgb = srgb ;
texture - > mipmaps = mipmaps ;
2018-09-09 05:28:30 +00:00
texture - > target = convertTextureTarget ( type ) ;
2019-08-29 21:26:44 +00:00
texture - > compareMode = COMPARE_NONE ;
2021-02-09 00:23:18 +00:00
state . stats . textureCount + + ;
2018-08-09 23:12:57 +00:00
WrapMode wrap = type = = TEXTURE_CUBE ? WRAP_CLAMP : WRAP_REPEAT ;
glGenTextures ( 1 , & texture - > id ) ;
lovrGpuBindTexture ( texture , 0 ) ;
lovrTextureSetWrap ( texture , ( TextureWrap ) { . s = wrap , . t = wrap , . r = wrap } ) ;
2020-01-31 11:35:42 +00:00
if ( msaa > 1 ) {
2018-08-29 11:28:12 +00:00
texture - > msaa = msaa ;
glGenRenderbuffers ( 1 , & texture - > msaaId ) ;
}
2018-08-09 23:12:57 +00:00
if ( sliceCount > 0 ) {
lovrTextureAllocate ( texture , slices [ 0 ] - > width , slices [ 0 ] - > height , sliceCount , slices [ 0 ] - > format ) ;
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < sliceCount ; i + + ) {
2018-08-09 23:12:57 +00:00
lovrTextureReplacePixels ( texture , slices [ i ] , 0 , 0 , i , 0 ) ;
}
}
return texture ;
}
2020-08-26 19:40:31 +00:00
Texture * lovrTextureCreateFromHandle ( uint32_t handle , TextureType type , uint32_t depth , uint32_t msaa ) {
2021-02-09 00:23:18 +00:00
Texture * texture = calloc ( 1 , sizeof ( Texture ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
2018-09-09 05:28:30 +00:00
texture - > type = type ;
texture - > id = handle ;
texture - > target = convertTextureTarget ( type ) ;
2020-02-19 23:33:41 +00:00
texture - > compareMode = COMPARE_NONE ;
2020-01-31 11:35:42 +00:00
texture - > native = true ;
2021-02-09 00:23:18 +00:00
state . stats . textureCount + + ;
2018-09-09 05:28:30 +00:00
2019-05-20 21:34:03 +00:00
int width , height ;
2018-09-09 17:58:20 +00:00
lovrGpuBindTexture ( texture , 0 ) ;
2019-05-20 21:34:03 +00:00
glGetTexLevelParameteriv ( texture - > target , 0 , GL_TEXTURE_WIDTH , & width ) ;
glGetTexLevelParameteriv ( texture - > target , 0 , GL_TEXTURE_HEIGHT , & height ) ;
texture - > width = ( uint32_t ) width ;
texture - > height = ( uint32_t ) height ;
2019-06-26 01:56:59 +00:00
texture - > depth = depth ; // There isn't an easy way to get depth/layer count, so it's passed in...
2019-06-26 01:59:31 +00:00
texture - > mipmapCount = 1 ;
2018-09-09 17:58:20 +00:00
2020-08-26 19:40:31 +00:00
if ( msaa > 1 ) {
texture - > msaa = msaa ;
GLint internalFormat ;
glGetTexLevelParameteriv ( texture - > target , 0 , GL_TEXTURE_INTERNAL_FORMAT , & internalFormat ) ;
glGenRenderbuffers ( 1 , & texture - > msaaId ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , texture - > msaaId ) ;
glRenderbufferStorageMultisample ( GL_RENDERBUFFER , texture - > msaa , internalFormat , width , height ) ;
}
2018-09-09 05:28:30 +00:00
return texture ;
}
2018-08-09 23:12:57 +00:00
void lovrTextureDestroy ( void * ref ) {
Texture * texture = ref ;
2021-04-28 04:24:42 +00:00
if ( ! texture - > native ) glDeleteTextures ( 1 , & texture - > id ) ;
2018-08-29 11:28:12 +00:00
glDeleteRenderbuffers ( 1 , & texture - > msaaId ) ;
2018-08-31 13:03:35 +00:00
lovrGpuDestroySyncResource ( texture , texture - > incoherent ) ;
2020-01-31 11:35:42 +00:00
state . stats . textureMemory - = getTextureMemorySize ( texture ) ;
state . stats . textureCount - - ;
2021-02-09 00:52:26 +00:00
free ( texture ) ;
2018-08-09 23:12:57 +00:00
}
2019-05-20 21:34:03 +00:00
void lovrTextureAllocate ( Texture * texture , uint32_t width , uint32_t height , uint32_t depth , TextureFormat format ) {
uint32_t maxSize = ( uint32_t ) state . limits . textureSize ;
2018-08-09 23:12:57 +00:00
lovrAssert ( ! texture - > allocated , " Texture is already allocated " ) ;
lovrAssert ( texture - > type ! = TEXTURE_CUBE | | width = = height , " Cubemap images must be square " ) ;
2021-02-07 23:58:50 +00:00
lovrAssert ( texture - > type ! = TEXTURE_CUBE | | depth = = 6 , " 6 images are required for a cube texture " ) ;
2018-08-09 23:12:57 +00:00
lovrAssert ( texture - > type ! = TEXTURE_2D | | depth = = 1 , " 2D textures can only contain a single image " ) ;
2022-01-10 08:03:01 +00:00
lovrAssert ( width < = maxSize , " Texture width %d exceeds max of %d " , width , maxSize ) ;
lovrAssert ( height < = maxSize , " Texture height %d exceeds max of %d " , height , maxSize ) ;
2018-08-29 11:28:12 +00:00
lovrAssert ( ! texture - > msaa | | texture - > type = = TEXTURE_2D , " Only 2D textures can be created with MSAA " ) ;
2018-08-09 23:12:57 +00:00
2018-07-17 10:35:01 +00:00
texture - > allocated = true ;
2018-08-09 23:12:57 +00:00
texture - > width = width ;
texture - > height = height ;
2018-08-22 04:08:40 +00:00
texture - > depth = depth ;
2018-08-09 23:12:57 +00:00
texture - > format = format ;
2018-07-20 02:28:27 +00:00
if ( texture - > mipmaps ) {
2019-05-20 21:34:03 +00:00
uint32_t dimension = texture - > type = = TEXTURE_VOLUME ? ( MAX ( MAX ( width , height ) , depth ) ) : MAX ( width , height ) ;
2018-07-20 02:28:27 +00:00
texture - > mipmapCount = texture - > mipmaps ? ( log2 ( dimension ) + 1 ) : 1 ;
} else {
texture - > mipmapCount = 1 ;
}
2018-07-17 06:51:11 +00:00
2018-08-09 23:12:57 +00:00
if ( isTextureFormatCompressed ( format ) ) {
2018-07-17 10:35:01 +00:00
return ;
}
2018-07-17 06:51:11 +00:00
2019-05-04 00:53:33 +00:00
GLenum internalFormat = convertTextureFormatInternal ( format , texture - > srgb ) ;
2020-01-15 03:39:10 +00:00
# ifdef LOVR_GL
2018-07-17 10:35:01 +00:00
if ( GLAD_GL_ARB_texture_storage ) {
# endif
2020-02-17 21:13:48 +00:00
if ( texture - > type = = TEXTURE_ARRAY | | texture - > type = = TEXTURE_VOLUME ) {
2018-08-09 23:12:57 +00:00
glTexStorage3D ( texture - > target , texture - > mipmapCount , internalFormat , width , height , depth ) ;
2018-07-17 10:35:01 +00:00
} else {
2018-08-09 23:12:57 +00:00
glTexStorage2D ( texture - > target , texture - > mipmapCount , internalFormat , width , height ) ;
2018-07-17 06:51:11 +00:00
}
2020-01-15 03:39:10 +00:00
# ifdef LOVR_GL
2018-07-17 10:35:01 +00:00
} else {
2020-03-03 06:23:59 +00:00
GLenum glFormat = convertTextureFormat ( format ) ;
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < texture - > mipmapCount ; i + + ) {
2018-07-17 10:35:01 +00:00
switch ( texture - > type ) {
case TEXTURE_2D :
2018-08-09 23:12:57 +00:00
glTexImage2D ( texture - > target , i , internalFormat , width , height , 0 , glFormat , GL_UNSIGNED_BYTE , NULL ) ;
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
case TEXTURE_CUBE :
2019-05-20 21:34:03 +00:00
for ( uint32_t face = 0 ; face < 6 ; face + + ) {
2018-08-09 23:12:57 +00:00
glTexImage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X + face , i , internalFormat , width , height , 0 , glFormat , GL_UNSIGNED_BYTE , NULL ) ;
2018-07-17 10:35:01 +00:00
}
break ;
case TEXTURE_ARRAY :
case TEXTURE_VOLUME :
2018-08-09 23:12:57 +00:00
glTexImage3D ( texture - > target , i , internalFormat , width , height , depth , 0 , glFormat , GL_UNSIGNED_BYTE , NULL ) ;
2018-07-17 10:35:01 +00:00
break ;
}
2018-08-09 23:12:57 +00:00
width = MAX ( width > > 1 , 1 ) ;
height = MAX ( height > > 1 , 1 ) ;
depth = texture - > type = = TEXTURE_VOLUME ? MAX ( depth > > 1 , 1 ) : depth ;
2018-07-17 10:35:01 +00:00
}
}
# endif
2018-08-29 11:28:12 +00:00
if ( texture - > msaaId ) {
glBindRenderbuffer ( GL_RENDERBUFFER , texture - > msaaId ) ;
glRenderbufferStorageMultisample ( GL_RENDERBUFFER , texture - > msaa , internalFormat , width , height ) ;
}
2020-01-31 11:35:42 +00:00
state . stats . textureMemory + = getTextureMemorySize ( texture ) ;
2018-07-17 06:51:11 +00:00
}
2021-02-09 03:17:47 +00:00
void lovrTextureReplacePixels ( Texture * texture , Image * image , uint32_t x , uint32_t y , uint32_t slice , uint32_t mipmap ) {
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2018-08-09 23:12:57 +00:00
lovrAssert ( texture - > allocated , " Texture is not allocated " ) ;
2018-07-17 06:51:11 +00:00
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-08-12 04:17:22 +00:00
if ( ( texture - > incoherent > > BARRIER_TEXTURE ) & 1 ) {
2018-08-31 13:03:35 +00:00
lovrGpuSync ( 1 < < BARRIER_TEXTURE ) ;
2018-08-12 04:17:22 +00:00
}
2018-08-31 13:03:35 +00:00
# endif
2018-08-12 04:17:22 +00:00
2019-05-20 21:34:03 +00:00
uint32_t maxWidth = lovrTextureGetWidth ( texture , mipmap ) ;
uint32_t maxHeight = lovrTextureGetHeight ( texture , mipmap ) ;
2021-02-09 03:17:47 +00:00
uint32_t width = image - > width ;
uint32_t height = image - > height ;
2018-08-16 21:28:10 +00:00
bool overflow = ( x + width > maxWidth ) | | ( y + height > maxHeight ) ;
2018-07-23 02:42:39 +00:00
lovrAssert ( ! overflow , " Trying to replace pixels outside the texture's bounds " ) ;
2019-12-13 11:41:35 +00:00
lovrAssert ( mipmap < texture - > mipmapCount , " Invalid mipmap level %d " , mipmap ) ;
2021-02-09 03:17:47 +00:00
GLenum glFormat = convertTextureFormat ( image - > format ) ;
GLenum glInternalFormat = convertTextureFormatInternal ( image - > format , texture - > srgb ) ;
2018-08-09 23:12:57 +00:00
GLenum binding = ( texture - > type = = TEXTURE_CUBE ) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice : texture - > target ;
2018-07-17 06:51:11 +00:00
2018-08-09 23:12:57 +00:00
lovrGpuBindTexture ( texture , 0 ) ;
2021-02-09 03:17:47 +00:00
if ( isTextureFormatCompressed ( image - > format ) ) {
2018-08-16 21:28:10 +00:00
lovrAssert ( width = = maxWidth & & height = = maxHeight , " Compressed texture pixels must be fully replaced " ) ;
2018-07-23 02:42:39 +00:00
lovrAssert ( mipmap = = 0 , " Unable to replace a specific mipmap of a compressed texture " ) ;
2021-02-09 03:17:47 +00:00
for ( uint32_t i = 0 ; i < image - > mipmapCount ; i + + ) {
Mipmap * m = image - > mipmaps + i ;
2018-07-17 10:35:01 +00:00
switch ( texture - > type ) {
case TEXTURE_2D :
case TEXTURE_CUBE :
2019-08-01 00:51:49 +00:00
glCompressedTexImage2D ( binding , i , glInternalFormat , m - > width , m - > height , 0 , ( GLsizei ) m - > size , m - > data ) ;
2018-07-17 10:35:01 +00:00
break ;
case TEXTURE_ARRAY :
case TEXTURE_VOLUME :
2019-08-01 00:51:49 +00:00
glCompressedTexSubImage3D ( binding , i , x , y , slice , m - > width , m - > height , 1 , glInternalFormat , ( GLsizei ) m - > size , m - > data ) ;
2018-07-17 10:35:01 +00:00
break ;
}
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
} else {
2021-02-09 03:17:47 +00:00
lovrAssert ( image - > blob - > data , " Trying to replace Texture pixels with empty pixel data " ) ;
GLenum glType = convertTextureFormatType ( image - > format ) ;
2018-09-01 12:40:45 +00:00
2018-07-17 10:35:01 +00:00
switch ( texture - > type ) {
case TEXTURE_2D :
case TEXTURE_CUBE :
2021-02-09 03:17:47 +00:00
glTexSubImage2D ( binding , mipmap , x , y , width , height , glFormat , glType , image - > blob - > data ) ;
2018-07-17 06:51:11 +00:00
break ;
2018-07-17 10:35:01 +00:00
case TEXTURE_ARRAY :
case TEXTURE_VOLUME :
2021-02-09 03:17:47 +00:00
glTexSubImage3D ( binding , mipmap , x , y , slice , width , height , 1 , glFormat , glType , image - > blob - > data ) ;
2018-07-17 06:51:11 +00:00
break ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
if ( texture - > mipmaps ) {
2019-01-29 23:05:23 +00:00
# if defined(__APPLE__) || defined(LOVR_WEBGL) // glGenerateMipmap doesn't work on big cubemap textures on macOS
2018-09-05 10:48:44 +00:00
if ( texture - > type ! = TEXTURE_CUBE | | width < 2048 ) {
glGenerateMipmap ( texture - > target ) ;
} else {
glTexParameteri ( texture - > target , GL_TEXTURE_MAX_LEVEL , 0 ) ;
}
# else
2018-08-09 23:12:57 +00:00
glGenerateMipmap ( texture - > target ) ;
2018-09-05 10:48:44 +00:00
# endif
2018-07-17 10:35:01 +00:00
}
}
}
2018-07-17 06:51:11 +00:00
2020-02-23 09:01:34 +00:00
uint64_t lovrTextureGetId ( Texture * texture ) {
return texture - > id ;
}
2020-02-23 06:03:20 +00:00
uint32_t lovrTextureGetWidth ( Texture * texture , uint32_t mipmap ) {
return MAX ( texture - > width > > mipmap , 1 ) ;
}
uint32_t lovrTextureGetHeight ( Texture * texture , uint32_t mipmap ) {
return MAX ( texture - > height > > mipmap , 1 ) ;
}
uint32_t lovrTextureGetDepth ( Texture * texture , uint32_t mipmap ) {
return texture - > type = = TEXTURE_VOLUME ? MAX ( texture - > depth > > mipmap , 1 ) : texture - > depth ;
}
uint32_t lovrTextureGetMipmapCount ( Texture * texture ) {
return texture - > mipmapCount ;
}
uint32_t lovrTextureGetMSAA ( Texture * texture ) {
return texture - > msaa ;
}
TextureType lovrTextureGetType ( Texture * texture ) {
return texture - > type ;
}
TextureFormat lovrTextureGetFormat ( Texture * texture ) {
return texture - > format ;
}
CompareMode lovrTextureGetCompareMode ( Texture * texture ) {
return texture - > compareMode ;
}
TextureFilter lovrTextureGetFilter ( Texture * texture ) {
return texture - > filter ;
}
TextureWrap lovrTextureGetWrap ( Texture * texture ) {
return texture - > wrap ;
}
2019-08-29 21:26:44 +00:00
void lovrTextureSetCompareMode ( Texture * texture , CompareMode compareMode ) {
if ( texture - > compareMode ! = compareMode ) {
lovrGraphicsFlush ( ) ;
lovrGpuBindTexture ( texture , 0 ) ;
texture - > compareMode = compareMode ;
if ( compareMode = = COMPARE_NONE ) {
glTexParameteri ( texture - > target , GL_TEXTURE_COMPARE_MODE , GL_NONE ) ;
} else {
lovrAssert ( isTextureFormatDepth ( texture - > format ) , " Only depth textures can set a compare mode " ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_COMPARE_MODE , GL_COMPARE_REF_TO_TEXTURE ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_COMPARE_FUNC , convertCompareMode ( compareMode ) ) ;
}
}
}
2018-07-17 10:35:01 +00:00
void lovrTextureSetFilter ( Texture * texture , TextureFilter filter ) {
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2018-07-17 10:35:01 +00:00
lovrGpuBindTexture ( texture , 0 ) ;
texture - > filter = filter ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
switch ( filter . mode ) {
case FILTER_NEAREST :
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
case FILTER_BILINEAR :
if ( texture - > mipmaps ) {
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_NEAREST ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2018-07-17 10:35:01 +00:00
} else {
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
case FILTER_TRILINEAR :
if ( texture - > mipmaps ) {
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2018-07-17 10:35:01 +00:00
} else {
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
}
2020-03-05 17:46:49 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MAX_ANISOTROPY_EXT , MAX ( filter . anisotropy , 1.f ) ) ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
void lovrTextureSetWrap ( Texture * texture , TextureWrap wrap ) {
2018-12-27 21:50:42 +00:00
lovrGraphicsFlush ( ) ;
2018-07-17 10:35:01 +00:00
texture - > wrap = wrap ;
lovrGpuBindTexture ( texture , 0 ) ;
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_WRAP_S , convertWrapMode ( wrap . s ) ) ;
glTexParameteri ( texture - > target , GL_TEXTURE_WRAP_T , convertWrapMode ( wrap . t ) ) ;
2018-07-17 10:35:01 +00:00
if ( texture - > type = = TEXTURE_CUBE | | texture - > type = = TEXTURE_VOLUME ) {
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_WRAP_R , convertWrapMode ( wrap . r ) ) ;
2018-07-17 06:51:11 +00:00
}
}
2018-07-17 10:35:01 +00:00
// Canvas
2018-07-17 06:51:11 +00:00
2020-02-23 06:19:59 +00:00
Canvas * lovrCanvasCreate ( uint32_t width , uint32_t height , CanvasFlags flags ) {
2021-02-09 00:23:18 +00:00
Canvas * canvas = calloc ( 1 , sizeof ( Canvas ) ) ;
lovrAssert ( canvas , " Out of memory " ) ;
canvas - > ref = 1 ;
2019-06-25 08:06:55 +00:00
if ( flags . stereo & & state . singlepass ! = MULTIVIEW ) {
width * = 2 ;
}
2018-08-24 21:43:39 +00:00
canvas - > width = width ;
canvas - > height = height ;
canvas - > flags = flags ;
2018-08-22 04:08:40 +00:00
glGenFramebuffers ( 1 , & canvas - > framebuffer ) ;
2018-08-24 21:43:39 +00:00
lovrGpuBindFramebuffer ( canvas - > framebuffer ) ;
2018-09-28 01:34:43 +00:00
if ( flags . depth . enabled ) {
lovrAssert ( isTextureFormatDepth ( flags . depth . format ) , " Canvas depth buffer can't use a color TextureFormat " ) ;
GLenum attachment = flags . depth . format = = FORMAT_D24S8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT ;
2019-06-25 08:06:55 +00:00
if ( flags . stereo & & state . singlepass = = MULTIVIEW ) {
2020-01-14 23:59:50 +00:00
// Zero MSAA is intentional here, we attach it to the Canvas using legacy MSAA technique
canvas - > depth . texture = lovrTextureCreate ( TEXTURE_ARRAY , NULL , 0 , false , flags . mipmaps , 0 ) ;
2019-06-25 08:06:55 +00:00
lovrTextureAllocate ( canvas - > depth . texture , width , height , 2 , flags . depth . format ) ;
2019-07-29 00:28:07 +00:00
# ifdef LOVR_WEBGL
lovrThrow ( " Unreachable " ) ;
# else
2019-06-25 08:06:55 +00:00
glFramebufferTextureMultisampleMultiviewOVR ( GL_FRAMEBUFFER , attachment , canvas - > depth . texture - > id , 0 , flags . msaa , 0 , 2 ) ;
2019-07-29 00:28:07 +00:00
# endif
2019-06-25 08:06:55 +00:00
} else if ( flags . depth . readable ) {
2018-09-28 01:34:43 +00:00
canvas - > depth . texture = lovrTextureCreate ( TEXTURE_2D , NULL , 0 , false , flags . mipmaps , flags . msaa ) ;
lovrTextureAllocate ( canvas - > depth . texture , width , height , 1 , flags . depth . format ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , attachment , GL_TEXTURE_2D , canvas - > depth . texture - > id , 0 ) ;
} else {
GLenum format = convertTextureFormatInternal ( flags . depth . format , false ) ;
glGenRenderbuffers ( 1 , & canvas - > depthBuffer ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , canvas - > depthBuffer ) ;
glRenderbufferStorageMultisample ( GL_RENDERBUFFER , canvas - > flags . msaa , format , width , height ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , attachment , GL_RENDERBUFFER , canvas - > depthBuffer ) ;
}
2018-08-24 21:43:39 +00:00
}
2018-08-22 04:08:40 +00:00
2020-01-14 23:59:50 +00:00
if ( flags . msaa & & ( ! flags . stereo | | state . singlepass ! = MULTIVIEW ) ) {
2018-12-25 05:46:50 +00:00
glGenFramebuffers ( 1 , & canvas - > resolveBuffer ) ;
2018-08-29 11:28:12 +00:00
}
2018-12-25 05:46:50 +00:00
return canvas ;
}
2018-08-29 11:28:12 +00:00
2020-02-23 06:19:59 +00:00
Canvas * lovrCanvasCreateFromHandle ( uint32_t width , uint32_t height , CanvasFlags flags , uint32_t framebuffer , uint32_t depthBuffer , uint32_t resolveBuffer , uint32_t attachmentCount , bool immortal ) {
2021-02-09 00:23:18 +00:00
Canvas * canvas = calloc ( 1 , sizeof ( Canvas ) ) ;
lovrAssert ( canvas , " Out of memory " ) ;
canvas - > ref = 1 ;
2018-12-25 05:46:50 +00:00
canvas - > framebuffer = framebuffer ;
canvas - > depthBuffer = depthBuffer ;
canvas - > resolveBuffer = resolveBuffer ;
canvas - > attachmentCount = attachmentCount ;
canvas - > width = width ;
canvas - > height = height ;
canvas - > flags = flags ;
canvas - > immortal = immortal ;
return canvas ;
}
2018-08-24 01:28:37 +00:00
2018-12-25 05:46:50 +00:00
void lovrCanvasDestroy ( void * ref ) {
Canvas * canvas = ref ;
2019-01-04 09:35:29 +00:00
lovrGraphicsFlushCanvas ( canvas ) ;
2018-12-25 05:46:50 +00:00
if ( ! canvas - > immortal ) {
glDeleteFramebuffers ( 1 , & canvas - > framebuffer ) ;
glDeleteRenderbuffers ( 1 , & canvas - > depthBuffer ) ;
glDeleteFramebuffers ( 1 , & canvas - > resolveBuffer ) ;
2018-08-24 01:28:37 +00:00
}
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( canvas - > attachments [ i ] . texture , lovrTextureDestroy ) ;
2018-08-24 22:11:37 +00:00
}
2021-02-09 00:52:26 +00:00
lovrRelease ( canvas - > depth . texture , lovrTextureDestroy ) ;
free ( canvas ) ;
2018-08-24 01:28:37 +00:00
}
2018-08-29 19:40:19 +00:00
void lovrCanvasResolve ( Canvas * canvas ) {
2018-08-29 21:08:03 +00:00
if ( ! canvas - > needsResolve ) {
2018-08-29 19:40:19 +00:00
return ;
}
2018-12-27 21:50:42 +00:00
lovrGraphicsFlushCanvas ( canvas ) ;
2018-12-15 00:00:37 +00:00
2020-01-14 23:59:50 +00:00
// We don't need to resolve a multiview Canvas because it uses the legacy multisampling method in
// which the driver does an implicit multisample resolve whenever the canvas textures are read.
if ( canvas - > flags . msaa & & ( ! canvas - > flags . stereo | | state . singlepass ! = MULTIVIEW ) ) {
2019-05-20 21:34:03 +00:00
uint32_t w = canvas - > width ;
uint32_t h = canvas - > height ;
2018-08-29 21:08:03 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , canvas - > framebuffer ) ;
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , canvas - > resolveBuffer ) ;
state . framebuffer = canvas - > resolveBuffer ;
2018-08-29 20:20:13 +00:00
2018-10-24 15:19:46 +00:00
if ( canvas - > attachmentCount = = 1 ) {
2018-08-29 20:20:13 +00:00
glBlitFramebuffer ( 0 , 0 , w , h , 0 , 0 , w , h , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
2018-08-29 21:08:03 +00:00
} else {
GLenum buffers [ MAX_CANVAS_ATTACHMENTS ] = { GL_NONE } ;
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2018-08-29 21:08:03 +00:00
buffers [ i ] = GL_COLOR_ATTACHMENT0 + i ;
glReadBuffer ( i ) ;
glDrawBuffers ( 1 , & buffers [ i ] ) ;
glBlitFramebuffer ( 0 , 0 , w , h , 0 , 0 , w , h , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
}
glReadBuffer ( 0 ) ;
2018-10-24 15:19:46 +00:00
glDrawBuffers ( canvas - > attachmentCount , buffers ) ;
2018-08-29 20:20:13 +00:00
}
}
2018-08-29 20:34:54 +00:00
if ( canvas - > flags . mipmaps ) {
2019-05-20 21:34:03 +00:00
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2018-08-29 20:34:54 +00:00
Texture * texture = canvas - > attachments [ i ] . texture ;
if ( texture - > mipmapCount > 1 ) {
lovrGpuBindTexture ( texture , 0 ) ;
glGenerateMipmap ( texture - > target ) ;
}
}
}
2018-08-29 21:08:03 +00:00
canvas - > needsResolve = false ;
2018-08-29 20:20:13 +00:00
}
2021-02-09 03:17:47 +00:00
Image * lovrCanvasNewImage ( Canvas * canvas , uint32_t index ) {
2018-12-27 21:50:42 +00:00
lovrGraphicsFlushCanvas ( canvas ) ;
2018-12-25 05:46:50 +00:00
lovrGpuBindCanvas ( canvas , false ) ;
2018-08-29 21:03:14 +00:00
2020-05-26 15:53:26 +00:00
if ( canvas - > flags . msaa ) {
glBindFramebuffer ( GL_READ_FRAMEBUFFER , canvas - > resolveBuffer ) ;
}
2018-08-29 21:03:14 +00:00
Texture * texture = canvas - > attachments [ index ] . texture ;
2022-03-04 02:14:35 +00:00
# ifndef LOVR_WEBGL
2018-08-29 21:03:14 +00:00
if ( ( texture - > incoherent > > BARRIER_TEXTURE ) & 1 ) {
2018-08-31 13:03:35 +00:00
lovrGpuSync ( 1 < < BARRIER_TEXTURE ) ;
2018-08-29 21:03:14 +00:00
}
2018-08-31 13:03:35 +00:00
# endif
2018-08-29 21:03:14 +00:00
if ( index ! = 0 ) {
glReadBuffer ( index ) ;
}
2022-03-04 02:14:35 +00:00
Image * image = lovrImageCreate ( canvas - > width , canvas - > height , NULL , 0x0 , texture - > format ) ;
GLenum glFormat = convertTextureFormat ( texture - > format ) ;
GLenum glFormatType = convertTextureFormatType ( texture - > format ) ;
glReadPixels ( 0 , 0 , canvas - > width , canvas - > height , glFormat , glFormatType , image - > blob - > data ) ;
2018-08-29 21:03:14 +00:00
if ( index ! = 0 ) {
glReadBuffer ( 0 ) ;
}
2021-02-09 03:17:47 +00:00
return image ;
2018-08-29 21:03:14 +00:00
}
2020-02-23 06:19:59 +00:00
const Attachment * lovrCanvasGetAttachments ( Canvas * canvas , uint32_t * count ) {
if ( count ) * count = canvas - > attachmentCount ;
return canvas - > attachments ;
}
void lovrCanvasSetAttachments ( Canvas * canvas , Attachment * attachments , uint32_t count ) {
lovrAssert ( count > 0 , " A Canvas must have at least one attached Texture " ) ;
2021-02-07 23:58:50 +00:00
lovrAssert ( count < = MAX_CANVAS_ATTACHMENTS , " Only %d textures can be attached to a Canvas, got %d " , MAX_CANVAS_ATTACHMENTS , count ) ;
2020-02-23 06:19:59 +00:00
if ( ! canvas - > needsAttach & & count = = canvas - > attachmentCount & & ! memcmp ( canvas - > attachments , attachments , count * sizeof ( Attachment ) ) ) {
return ;
}
lovrGraphicsFlushCanvas ( canvas ) ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
Texture * texture = attachments [ i ] . texture ;
uint32_t slice = attachments [ i ] . slice ;
uint32_t level = attachments [ i ] . level ;
uint32_t width = lovrTextureGetWidth ( texture , level ) ;
uint32_t height = lovrTextureGetHeight ( texture , level ) ;
uint32_t depth = lovrTextureGetDepth ( texture , level ) ;
uint32_t mipmaps = lovrTextureGetMipmapCount ( texture ) ;
bool hasDepthBuffer = canvas - > flags . depth . enabled ;
lovrAssert ( slice < depth , " Invalid attachment slice (Texture has %d, got %d) " , depth , slice + 1 ) ;
lovrAssert ( level < mipmaps , " Invalid attachment mipmap level (Texture has %d, got %d) " , mipmaps , level + 1 ) ;
lovrAssert ( ! hasDepthBuffer | | width = = canvas - > width , " Texture width of %d does not match Canvas width (%d) " , width , canvas - > width ) ;
lovrAssert ( ! hasDepthBuffer | | height = = canvas - > height , " Texture height of %d does not match Canvas height (%d) " , height , canvas - > height ) ;
# ifndef __ANDROID__ // On multiview canvases, the multisample settings can be different
lovrAssert ( lovrTextureGetMSAA ( texture ) = = canvas - > flags . msaa , " Texture MSAA does not match Canvas MSAA " ) ;
# endif
lovrRetain ( texture ) ;
}
for ( uint32_t i = 0 ; i < canvas - > attachmentCount ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( canvas - > attachments [ i ] . texture , lovrTextureDestroy ) ;
2020-02-23 06:19:59 +00:00
}
memcpy ( canvas - > attachments , attachments , count * sizeof ( Attachment ) ) ;
canvas - > attachmentCount = count ;
canvas - > needsAttach = true ;
}
bool lovrCanvasIsStereo ( Canvas * canvas ) {
return canvas - > flags . stereo ;
}
void lovrCanvasSetStereo ( Canvas * canvas , bool stereo ) {
canvas - > flags . stereo = stereo ;
}
uint32_t lovrCanvasGetWidth ( Canvas * canvas ) {
return canvas - > width ;
}
uint32_t lovrCanvasGetHeight ( Canvas * canvas ) {
return canvas - > height ;
}
void lovrCanvasSetWidth ( Canvas * canvas , uint32_t width ) {
canvas - > width = width ;
}
void lovrCanvasSetHeight ( Canvas * canvas , uint32_t height ) {
canvas - > height = height ;
}
uint32_t lovrCanvasGetMSAA ( Canvas * canvas ) {
return canvas - > flags . msaa ;
}
Texture * lovrCanvasGetDepthTexture ( Canvas * canvas ) {
return canvas - > depth . texture ;
}
2018-12-07 00:14:30 +00:00
// Buffer
2020-02-23 05:43:36 +00:00
Buffer * lovrBufferCreate ( size_t size , void * data , BufferType type , BufferUsage usage , bool readable ) {
2021-02-09 00:23:18 +00:00
Buffer * buffer = calloc ( 1 , sizeof ( Buffer ) ) ;
lovrAssert ( buffer , " Out of memory " ) ;
buffer - > ref = 1 ;
2020-01-31 11:35:42 +00:00
state . stats . bufferCount + + ;
state . stats . bufferMemory + = size ;
2018-12-07 00:14:30 +00:00
buffer - > size = size ;
2018-12-27 21:50:42 +00:00
buffer - > readable = readable ;
buffer - > type = type ;
2018-12-07 00:14:30 +00:00
buffer - > usage = usage ;
glGenBuffers ( 1 , & buffer - > id ) ;
2019-02-01 01:24:13 +00:00
lovrGpuBindBuffer ( type , buffer - > id ) ;
2018-12-27 21:50:42 +00:00
GLenum glType = convertBufferType ( type ) ;
2018-12-07 00:14:30 +00:00
2021-04-13 16:52:28 +00:00
# ifndef LOVR_WEBGL
if ( state . amd ) {
# endif
buffer - > data = malloc ( size ) ;
lovrAssert ( buffer - > data , " Out of memory " ) ;
glBufferData ( glType , size , data , convertBufferUsage ( usage ) ) ;
2018-12-07 00:14:30 +00:00
2021-04-13 16:52:28 +00:00
if ( data ) {
memcpy ( buffer - > data , data , size ) ;
}
# ifndef LOVR_WEBGL
} else {
glBufferData ( glType , size , data , convertBufferUsage ( usage ) ) ;
2018-12-07 00:14:30 +00:00
}
# endif
return buffer ;
}
void lovrBufferDestroy ( void * ref ) {
Buffer * buffer = ref ;
2018-12-27 21:50:42 +00:00
lovrGpuDestroySyncResource ( buffer , buffer - > incoherent ) ;
2018-12-07 00:14:30 +00:00
glDeleteBuffers ( 1 , & buffer - > id ) ;
2021-04-13 16:52:28 +00:00
# ifndef LOVR_WEBGL
if ( state . amd )
2018-12-14 23:13:21 +00:00
# endif
2021-04-13 16:52:28 +00:00
free ( buffer - > data ) ;
2020-01-31 11:35:42 +00:00
state . stats . bufferMemory - = buffer - > size ;
state . stats . bufferCount - - ;
2021-02-09 00:52:26 +00:00
free ( buffer ) ;
2019-05-14 03:35:21 +00:00
}
2020-02-23 05:43:36 +00:00
size_t lovrBufferGetSize ( Buffer * buffer ) {
return buffer - > size ;
}
bool lovrBufferIsReadable ( Buffer * buffer ) {
return buffer - > readable ;
}
BufferUsage lovrBufferGetUsage ( Buffer * buffer ) {
return buffer - > usage ;
}
2020-08-17 09:29:30 +00:00
void * lovrBufferMap ( Buffer * buffer , size_t offset , bool unsynchronized ) {
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2021-04-13 16:52:28 +00:00
if ( ! state . amd & & ! buffer - > mapped ) {
2019-05-14 03:35:21 +00:00
buffer - > mapped = true ;
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
2020-08-17 09:29:30 +00:00
lovrAssert ( ! buffer - > readable | | ! unsynchronized , " Readable Buffers must be mapped with synchronization " ) ;
2019-11-14 20:42:52 +00:00
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT ;
2020-08-17 09:29:30 +00:00
flags | = buffer - > readable ? GL_MAP_READ_BIT : 0 ;
flags | = unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0 ;
2019-05-14 03:35:21 +00:00
buffer - > data = glMapBufferRange ( convertBufferType ( buffer - > type ) , 0 , buffer - > size , flags ) ;
2021-04-13 16:52:28 +00:00
return ( uint8_t * ) buffer - > data + offset ;
2018-12-07 00:14:30 +00:00
}
2018-12-14 23:13:21 +00:00
# endif
2019-05-14 03:35:21 +00:00
return ( uint8_t * ) buffer - > data + offset ;
2018-12-07 00:14:30 +00:00
}
2019-05-14 03:35:21 +00:00
void lovrBufferFlush ( Buffer * buffer , size_t offset , size_t size ) {
2020-11-02 06:11:35 +00:00
# ifndef LOVR_WEBGL
2021-04-13 16:52:28 +00:00
lovrAssert ( state . amd | | size = = 0 | | buffer - > mapped , " Attempt to flush unmapped Buffer " ) ;
2020-11-02 06:11:35 +00:00
# endif
2019-05-14 03:35:21 +00:00
buffer - > flushFrom = MIN ( buffer - > flushFrom , offset ) ;
buffer - > flushTo = MAX ( buffer - > flushTo , offset + size ) ;
2018-12-07 00:14:30 +00:00
}
2019-05-14 03:35:21 +00:00
void lovrBufferUnmap ( Buffer * buffer ) {
2021-04-13 16:52:28 +00:00
# ifndef LOVR_WEBGL
if ( state . amd ) {
# endif
if ( buffer - > flushTo > buffer - > flushFrom ) {
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
void * data = ( uint8_t * ) buffer - > data + buffer - > flushFrom ;
glBufferSubData ( convertBufferType ( buffer - > type ) , buffer - > flushFrom , buffer - > flushTo - buffer - > flushFrom , data ) ;
}
# ifndef LOVR_WEBGL
} else if ( buffer - > mapped ) {
2019-05-14 03:35:21 +00:00
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
2019-07-13 03:16:18 +00:00
if ( buffer - > flushTo > buffer - > flushFrom ) {
glFlushMappedBufferRange ( convertBufferType ( buffer - > type ) , buffer - > flushFrom , buffer - > flushTo - buffer - > flushFrom ) ;
}
2019-05-14 03:35:21 +00:00
2020-08-17 09:29:30 +00:00
glUnmapBuffer ( convertBufferType ( buffer - > type ) ) ;
buffer - > mapped = false ;
2018-12-07 00:14:30 +00:00
}
# endif
2019-05-14 03:35:21 +00:00
buffer - > flushFrom = SIZE_MAX ;
buffer - > flushTo = 0 ;
2018-12-07 00:14:30 +00:00
}
2019-06-25 07:09:48 +00:00
void lovrBufferDiscard ( Buffer * buffer ) {
2020-08-17 09:29:30 +00:00
lovrAssert ( ! buffer - > readable , " Readable Buffers can not be discarded " ) ;
2020-09-20 00:21:37 +00:00
lovrAssert ( ! buffer - > mapped , " Mapped Buffers can not be discarded " ) ;
2019-06-25 07:09:48 +00:00
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
GLenum glType = convertBufferType ( buffer - > type ) ;
2021-04-13 16:52:28 +00:00
# ifndef LOVR_WEBGL
if ( state . amd ) {
# endif
glBufferData ( glType , buffer - > size , NULL , convertBufferUsage ( buffer - > usage ) ) ;
# ifndef LOVR_WEBGL
} else {
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ;
buffer - > data = glMapBufferRange ( glType , 0 , buffer - > size , flags ) ;
buffer - > mapped = true ;
}
2019-06-25 07:09:48 +00:00
# endif
}
2018-07-17 10:35:01 +00:00
// Shader
2018-07-17 06:51:11 +00:00
2020-01-27 21:07:41 +00:00
static GLuint compileShader ( GLenum type , const char * * sources , int * lengths , int count ) {
2018-07-17 10:35:01 +00:00
GLuint shader = glCreateShader ( type ) ;
2020-01-27 21:07:41 +00:00
glShaderSource ( shader , count , sources , lengths ) ;
2018-07-17 10:35:01 +00:00
glCompileShader ( shader ) ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
int isShaderCompiled ;
glGetShaderiv ( shader , GL_COMPILE_STATUS , & isShaderCompiled ) ;
if ( ! isShaderCompiled ) {
int logLength ;
glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , & logLength ) ;
char * log = malloc ( logLength ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( log , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
glGetShaderInfoLog ( shader , logLength , & logLength , log ) ;
2019-03-09 16:14:39 +00:00
const char * name ;
switch ( type ) {
case GL_VERTEX_SHADER : name = " vertex shader " ; break ;
case GL_FRAGMENT_SHADER : name = " fragment shader " ; break ;
case GL_COMPUTE_SHADER : name = " compute shader " ; break ;
default : name = " shader " ; break ;
}
lovrThrow ( " Could not compile %s: \n %s " , name , log ) ;
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
return shader ;
2018-07-17 06:51:11 +00:00
}
2018-08-07 23:58:14 +00:00
static GLuint linkProgram ( GLuint program ) {
2018-07-17 10:35:01 +00:00
glLinkProgram ( program ) ;
int isLinked ;
glGetProgramiv ( program , GL_LINK_STATUS , & isLinked ) ;
if ( ! isLinked ) {
int logLength ;
glGetProgramiv ( program , GL_INFO_LOG_LENGTH , & logLength ) ;
char * log = malloc ( logLength ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( log , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
glGetProgramInfoLog ( program , logLength , & logLength , log ) ;
2018-08-11 07:04:09 +00:00
lovrThrow ( " Could not link shader: \n %s " , log ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
return program ;
2018-07-17 06:51:11 +00:00
}
2018-08-07 23:58:14 +00:00
static void lovrShaderSetupUniforms ( Shader * shader ) {
uint32_t program = shader - > program ;
lovrGpuUseProgram ( program ) ; // TODO necessary?
2018-07-17 10:35:01 +00:00
2018-07-28 22:40:13 +00:00
// Uniform blocks
int32_t blockCount ;
glGetProgramiv ( program , GL_ACTIVE_UNIFORM_BLOCKS , & blockCount ) ;
2019-07-09 16:06:11 +00:00
lovrAssert ( ( size_t ) blockCount < = MAX_BLOCK_BUFFERS , " Shader has too many uniform blocks (%d) the max is %d " , blockCount , MAX_BLOCK_BUFFERS ) ;
2019-09-07 22:07:07 +00:00
map_init ( & shader - > blockMap , blockCount ) ;
2019-06-08 10:45:03 +00:00
arr_block_t * uniformBlocks = & shader - > blocks [ BLOCK_UNIFORM ] ;
2022-03-14 19:27:58 +00:00
arr_init ( uniformBlocks , arr_alloc ) ;
2019-06-08 10:45:03 +00:00
arr_reserve ( uniformBlocks , ( size_t ) blockCount ) ;
2018-07-28 22:40:13 +00:00
for ( int i = 0 ; i < blockCount ; i + + ) {
2018-08-18 02:52:34 +00:00
UniformBlock block = { . slot = i , . source = NULL } ;
glUniformBlockBinding ( program , i , block . slot ) ;
2018-07-28 22:40:13 +00:00
2019-09-07 22:07:07 +00:00
GLsizei length ;
2018-07-28 22:40:13 +00:00
char name [ LOVR_MAX_UNIFORM_LENGTH ] ;
2019-09-07 22:07:07 +00:00
glGetActiveUniformBlockName ( program , i , LOVR_MAX_UNIFORM_LENGTH , & length , name ) ;
2018-08-03 00:17:26 +00:00
int blockId = ( i < < 1 ) + BLOCK_UNIFORM ;
2019-09-07 22:07:07 +00:00
map_set ( & shader - > blockMap , hash64 ( name , length ) , blockId ) ;
2019-06-08 10:45:03 +00:00
arr_push ( uniformBlocks , block ) ;
2022-03-14 19:27:58 +00:00
arr_init ( & uniformBlocks - > data [ uniformBlocks - > length - 1 ] . uniforms , arr_alloc ) ;
2018-08-03 00:17:26 +00:00
}
// Shader storage buffers and their buffer variables
2019-06-08 10:45:03 +00:00
arr_block_t * computeBlocks = & shader - > blocks [ BLOCK_COMPUTE ] ;
2022-03-14 19:27:58 +00:00
arr_init ( computeBlocks , arr_alloc ) ;
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2020-01-31 03:14:25 +00:00
if ( ( GLAD_GL_ARB_shader_storage_buffer_object & & GLAD_GL_ARB_program_interface_query ) | | GLAD_GL_ES_VERSION_3_1 ) {
2018-08-03 00:17:26 +00:00
2019-01-16 16:52:21 +00:00
// Iterate over compute blocks, setting their binding and pushing them onto the block vector
2019-06-08 10:45:03 +00:00
int32_t computeBlockCount ;
2019-01-16 16:52:21 +00:00
glGetProgramInterfaceiv ( program , GL_SHADER_STORAGE_BLOCK , GL_ACTIVE_RESOURCES , & computeBlockCount ) ;
lovrAssert ( computeBlockCount < = MAX_BLOCK_BUFFERS , " Shader has too many compute blocks (%d) the max is %d " , computeBlockCount , MAX_BLOCK_BUFFERS ) ;
2019-06-08 10:45:03 +00:00
arr_reserve ( computeBlocks , ( size_t ) computeBlockCount ) ;
2019-01-16 16:52:21 +00:00
for ( int i = 0 ; i < computeBlockCount ; i + + ) {
2018-08-18 02:52:34 +00:00
UniformBlock block = { . slot = i , . source = NULL } ;
2020-01-31 03:14:25 +00:00
# ifdef LOVR_GLES // GLES can only set the block binding in shader code, so for now we only support one 0-bound block
block . slot = 0 ;
# else
2018-08-18 02:52:34 +00:00
glShaderStorageBlockBinding ( program , i , block . slot ) ;
2020-01-31 03:14:25 +00:00
# endif
2022-03-14 19:27:58 +00:00
arr_init ( & block . uniforms , arr_alloc ) ;
2018-08-03 00:17:26 +00:00
2019-09-07 22:07:07 +00:00
GLsizei length ;
2018-08-03 00:17:26 +00:00
char name [ LOVR_MAX_UNIFORM_LENGTH ] ;
2019-09-07 22:07:07 +00:00
glGetProgramResourceName ( program , GL_SHADER_STORAGE_BLOCK , i , LOVR_MAX_UNIFORM_LENGTH , & length , name ) ;
2019-01-16 16:52:21 +00:00
int blockId = ( i < < 1 ) + BLOCK_COMPUTE ;
2019-09-07 22:07:07 +00:00
map_set ( & shader - > blockMap , hash64 ( name , length ) , blockId ) ;
2019-06-08 10:45:03 +00:00
arr_push ( computeBlocks , block ) ;
2018-08-03 00:17:26 +00:00
}
2018-08-03 18:19:40 +00:00
// Iterate over buffer variables, pushing them onto the uniform list of the correct block
int bufferVariableCount ;
glGetProgramInterfaceiv ( program , GL_BUFFER_VARIABLE , GL_ACTIVE_RESOURCES , & bufferVariableCount ) ;
for ( int i = 0 ; i < bufferVariableCount ; i + + ) {
Uniform uniform ;
enum { blockIndex , offset , glType , count , arrayStride , matrixStride , propCount } ;
int values [ propCount ] ;
GLenum properties [ propCount ] = { GL_BLOCK_INDEX , GL_OFFSET , GL_TYPE , GL_ARRAY_SIZE , GL_ARRAY_STRIDE , GL_MATRIX_STRIDE } ;
glGetProgramResourceiv ( program , GL_BUFFER_VARIABLE , i , propCount , properties , sizeof ( values ) , NULL , values ) ;
glGetProgramResourceName ( program , GL_BUFFER_VARIABLE , i , LOVR_MAX_UNIFORM_LENGTH , NULL , uniform . name ) ;
uniform . type = getUniformType ( values [ glType ] , uniform . name ) ;
uniform . components = getUniformComponents ( uniform . type ) ;
uniform . count = values [ count ] ;
uniform . offset = values [ offset ] ;
if ( uniform . count > 1 ) {
uniform . size = uniform . count * values [ arrayStride ] ;
} else if ( uniform . type = = UNIFORM_MATRIX ) {
uniform . size = values [ matrixStride ] * uniform . components ;
} else {
2018-08-03 19:32:32 +00:00
uniform . size = 4 * ( uniform . components = = 3 ? 4 : uniform . components ) ;
2018-08-03 18:19:40 +00:00
}
2019-06-08 10:45:03 +00:00
arr_push ( & computeBlocks - > data [ values [ blockIndex ] ] . uniforms , uniform ) ;
2018-08-03 18:19:40 +00:00
}
2018-07-28 22:40:13 +00:00
}
2018-08-06 15:51:06 +00:00
# endif
2018-07-28 22:40:13 +00:00
2018-07-17 10:35:01 +00:00
// Uniform introspection
int32_t uniformCount ;
int textureSlot = 0 ;
2018-08-18 02:52:34 +00:00
int imageSlot = 0 ;
2018-07-17 10:35:01 +00:00
glGetProgramiv ( program , GL_ACTIVE_UNIFORMS , & uniformCount ) ;
2019-09-07 22:07:07 +00:00
map_init ( & shader - > uniformMap , 0 ) ;
2022-03-14 19:27:58 +00:00
arr_init ( & shader - > uniforms , arr_alloc ) ;
2018-07-28 22:40:13 +00:00
for ( uint32_t i = 0 ; i < ( uint32_t ) uniformCount ; i + + ) {
2018-07-17 10:35:01 +00:00
Uniform uniform ;
2018-07-19 23:58:59 +00:00
GLenum glType ;
2019-09-07 22:07:07 +00:00
GLsizei length ;
glGetActiveUniform ( program , i , LOVR_MAX_UNIFORM_LENGTH , & length , & uniform . count , & glType , uniform . name ) ;
2018-07-17 10:35:01 +00:00
char * subscript = strchr ( uniform . name , ' [ ' ) ;
if ( subscript ) {
2019-01-11 20:27:40 +00:00
if ( subscript [ 1 ] > ' 0 ' ) {
continue ;
} else {
* subscript = ' \0 ' ;
2019-11-26 19:34:39 +00:00
length = subscript - uniform . name ;
2019-01-11 20:27:40 +00:00
}
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
uniform . location = glGetUniformLocation ( program , uniform . name ) ;
2018-07-19 23:58:59 +00:00
uniform . type = getUniformType ( glType , uniform . name ) ;
uniform . components = getUniformComponents ( glType ) ;
2019-08-29 21:26:44 +00:00
uniform . shadow = glType = = GL_SAMPLER_2D_SHADOW ;
2019-01-29 23:05:23 +00:00
# ifdef LOVR_WEBGL
2018-08-10 11:21:03 +00:00
uniform . image = false ;
# else
2018-08-10 10:37:02 +00:00
uniform . image = glType = = GL_IMAGE_2D | | glType = = GL_IMAGE_3D | | glType = = GL_IMAGE_CUBE | | glType = = GL_IMAGE_2D_ARRAY ;
2018-08-10 11:21:03 +00:00
# endif
2018-08-18 02:52:34 +00:00
uniform . textureType = getUniformTextureType ( glType ) ;
uniform . baseSlot = uniform . type = = UNIFORM_SAMPLER ? textureSlot : ( uniform . type = = UNIFORM_IMAGE ? imageSlot : - 1 ) ;
2019-11-28 23:31:27 +00:00
uniform . dirty = false ;
2018-07-17 06:51:11 +00:00
2018-07-28 22:40:13 +00:00
int blockIndex ;
glGetActiveUniformsiv ( program , 1 , & i , GL_UNIFORM_BLOCK_INDEX , & blockIndex ) ;
if ( blockIndex ! = - 1 ) {
2018-08-03 00:17:26 +00:00
UniformBlock * block = & shader - > blocks [ BLOCK_UNIFORM ] . data [ blockIndex ] ;
2018-07-28 22:40:13 +00:00
glGetActiveUniformsiv ( program , 1 , & i , GL_UNIFORM_OFFSET , & uniform . offset ) ;
2018-08-01 23:02:20 +00:00
glGetActiveUniformsiv ( program , 1 , & i , GL_UNIFORM_SIZE , & uniform . count ) ;
if ( uniform . count > 1 ) {
int stride ;
glGetActiveUniformsiv ( program , 1 , & i , GL_UNIFORM_ARRAY_STRIDE , & stride ) ;
uniform . size = stride * uniform . count ;
} else if ( uniform . type = = UNIFORM_MATRIX ) {
int matrixStride ;
glGetActiveUniformsiv ( program , 1 , & i , GL_UNIFORM_MATRIX_STRIDE , & matrixStride ) ;
uniform . size = uniform . components * matrixStride ;
} else {
2018-08-03 19:32:32 +00:00
uniform . size = 4 * ( uniform . components = = 3 ? 4 : uniform . components ) ;
2018-08-01 23:02:20 +00:00
}
2018-12-14 23:48:09 +00:00
2019-06-08 10:45:03 +00:00
arr_push ( & block - > uniforms , uniform ) ;
2018-07-28 22:40:13 +00:00
continue ;
} else if ( uniform . location = = - 1 ) {
2018-07-17 06:51:11 +00:00
continue ;
}
2018-07-17 10:35:01 +00:00
switch ( uniform . type ) {
case UNIFORM_FLOAT :
uniform . size = uniform . components * uniform . count * sizeof ( float ) ;
uniform . value . data = calloc ( 1 , uniform . size ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( uniform . value . data , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
case UNIFORM_INT :
uniform . size = uniform . components * uniform . count * sizeof ( int ) ;
uniform . value . data = calloc ( 1 , uniform . size ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( uniform . value . data , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
break ;
case UNIFORM_MATRIX :
2018-12-27 21:50:42 +00:00
uniform . size = uniform . components * uniform . components * uniform . count * sizeof ( float ) ;
2018-07-17 10:35:01 +00:00
uniform . value . data = calloc ( 1 , uniform . size ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( uniform . value . data , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
break ;
2018-08-18 02:52:34 +00:00
case UNIFORM_SAMPLER :
case UNIFORM_IMAGE :
2021-02-09 03:17:47 +00:00
uniform . size = uniform . count * ( uniform . type = = UNIFORM_SAMPLER ? sizeof ( Texture * ) : sizeof ( StorageImage ) ) ;
2018-07-17 10:35:01 +00:00
uniform . value . data = calloc ( 1 , uniform . size ) ;
2019-01-29 10:49:09 +00:00
lovrAssert ( uniform . value . data , " Out of memory " ) ;
2018-07-17 10:35:01 +00:00
// Use the value for ints to bind texture slots, but use the value for textures afterwards.
2020-01-23 19:18:04 +00:00
for ( int j = 0 ; j < uniform . count ; j + + ) {
uniform . value . ints [ j ] = uniform . baseSlot + j ;
2018-07-17 10:35:01 +00:00
}
glUniform1iv ( uniform . location , uniform . count , uniform . value . ints ) ;
2018-08-18 02:52:34 +00:00
memset ( uniform . value . data , 0 , uniform . size ) ;
2018-07-17 10:35:01 +00:00
break ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
size_t offset = 0 ;
for ( int j = 0 ; j < uniform . count ; j + + ) {
int location = uniform . location ;
if ( uniform . count > 1 ) {
2019-03-17 02:43:04 +00:00
char name [ 76 /* LOVR_MAX_UNIFORM_LENGTH + 2 + 10 */ ] ;
snprintf ( name , sizeof ( name ) , " %s[%d] " , uniform . name , j ) ;
2018-07-17 10:35:01 +00:00
location = glGetUniformLocation ( program , name ) ;
}
switch ( uniform . type ) {
2018-12-25 05:08:06 +00:00
case UNIFORM_FLOAT : glGetUniformfv ( program , location , & uniform . value . floats [ offset ] ) ; break ;
case UNIFORM_INT : glGetUniformiv ( program , location , & uniform . value . ints [ offset ] ) ; break ;
case UNIFORM_MATRIX : glGetUniformfv ( program , location , & uniform . value . floats [ offset ] ) ; break ;
2018-08-18 02:52:34 +00:00
default : break ;
2018-07-17 06:51:11 +00:00
}
2018-12-25 05:08:06 +00:00
offset + = uniform . components * ( uniform . type = = UNIFORM_MATRIX ? uniform . components : 1 ) ;
2018-07-17 06:51:11 +00:00
}
2019-09-07 22:07:07 +00:00
map_set ( & shader - > uniformMap , hash64 ( uniform . name , length ) , shader - > uniforms . length ) ;
2019-06-08 10:45:03 +00:00
arr_push ( & shader - > uniforms , uniform ) ;
2018-08-18 02:52:34 +00:00
textureSlot + = uniform . type = = UNIFORM_SAMPLER ? uniform . count : 0 ;
imageSlot + = uniform . type = = UNIFORM_IMAGE ? uniform . count : 0 ;
2018-07-17 06:51:11 +00:00
}
2018-08-07 23:58:14 +00:00
}
2019-05-14 10:48:01 +00:00
static char * lovrShaderGetFlagCode ( ShaderFlag * flags , uint32_t flagCount ) {
if ( flagCount = = 0 ) {
return NULL ;
}
// Figure out how much space we need
size_t length = 0 ;
for ( uint32_t i = 0 ; i < flagCount ; i + + ) {
2019-07-29 01:08:23 +00:00
if ( flags [ i ] . name & & ! ( flags [ i ] . type = = FLAG_BOOL & & flags [ i ] . value . b32 = = false ) ) {
2019-05-14 10:48:01 +00:00
length + = strlen ( " #define FLAG_ " ) ;
2019-07-29 01:08:23 +00:00
length + = strlen ( flags [ i ] . name ) ;
if ( flags [ i ] . type = = FLAG_INT ) {
length + = snprintf ( NULL , 0 , " %d " , flags [ i ] . value . i32 ) ;
2019-05-14 10:48:01 +00:00
}
length + = strlen ( " \n " ) ;
}
}
// Generate the string
char * code = malloc ( length + 1 ) ;
code [ length ] = ' \0 ' ;
char * s = code ;
for ( uint32_t i = 0 ; i < flagCount ; i + + ) {
2019-08-07 22:56:13 +00:00
if ( flags [ i ] . name & & ! ( flags [ i ] . type = = FLAG_BOOL & & flags [ i ] . value . b32 = = false ) ) {
2019-07-29 01:08:23 +00:00
s + = sprintf ( s , " #define FLAG_%s " , flags [ i ] . name ) ;
if ( flags [ i ] . type = = FLAG_INT ) {
2019-08-06 21:38:49 +00:00
s + = sprintf ( s , " %d " , flags [ i ] . value . i32 ) ;
2019-05-14 10:48:01 +00:00
}
2019-08-06 21:38:49 +00:00
* s + + = ' \n ' ;
2019-05-14 10:48:01 +00:00
}
}
return code ;
}
2020-02-23 06:42:05 +00:00
Shader * lovrShaderCreateGraphics ( const char * vertexSource , int vertexSourceLength , const char * fragmentSource , int fragmentSourceLength , ShaderFlag * flags , uint32_t flagCount , bool multiview ) {
2021-02-09 00:23:18 +00:00
Shader * shader = calloc ( 1 , sizeof ( Shader ) ) ;
lovrAssert ( shader , " Out of memory " ) ;
shader - > ref = 1 ;
2019-01-29 23:05:23 +00:00
# if defined(LOVR_WEBGL) || defined(LOVR_GLES)
2019-06-25 08:06:55 +00:00
const char * version = " #version 300 es \n " ;
2021-02-19 21:09:40 +00:00
const char * computeExtensions = " " ;
2018-09-01 08:57:38 +00:00
# else
2021-02-19 21:07:27 +00:00
const char * version = " #version 330 \n " ;
const char * computeExtensions = state . features . compute ?
2021-02-23 00:07:28 +00:00
" #extension GL_ARB_shader_storage_buffer_object : enable \n "
" #extension GL_ARB_shader_image_load_store : enable \n " :
" " ;
2021-02-19 21:09:40 +00:00
# endif
2021-02-19 21:07:27 +00:00
2019-06-25 08:06:55 +00:00
const char * singlepass [ 2 ] = { " " , " " } ;
if ( multiview & & state . singlepass = = MULTIVIEW ) {
singlepass [ 0 ] = singlepass [ 1 ] = " #extension GL_OVR_multiview2 : require \n #define MULTIVIEW \n " ;
} else if ( state . singlepass = = INSTANCED_STEREO ) {
singlepass [ 0 ] = " #extension GL_AMD_vertex_shader_viewport_index : require \n " " #define INSTANCED_STEREO \n " ;
2019-06-26 02:15:11 +00:00
singlepass [ 1 ] = " #extension GL_ARB_fragment_layer_viewport : require \n " " #define INSTANCED_STEREO \n " ;
2019-06-25 08:06:55 +00:00
}
2018-09-01 02:52:03 +00:00
2019-05-14 10:48:01 +00:00
char * flagSource = lovrShaderGetFlagCode ( flags , flagCount ) ;
2021-06-21 16:21:35 +00:00
if ( ! vertexSource ) {
vertexSource = lovrUnlitVertexShader ;
vertexSourceLength = - 1 ;
}
if ( ! fragmentSource ) {
fragmentSource = lovrUnlitFragmentShader ;
fragmentSourceLength = - 1 ;
}
2018-08-07 23:58:14 +00:00
// Vertex
2021-02-19 21:07:27 +00:00
const char * vertexSources [ ] = { version , computeExtensions , singlepass [ 0 ] , flagSource ? flagSource : " " , lovrShaderVertexPrefix , vertexSource , lovrShaderVertexSuffix } ;
int vertexSourceLengths [ ] = { - 1 , - 1 , - 1 , - 1 , - 1 , vertexSourceLength , - 1 } ;
2020-10-26 08:03:19 +00:00
int vertexSourceCount = sizeof ( vertexSources ) / sizeof ( vertexSources [ 0 ] ) ;
2020-01-27 21:07:41 +00:00
GLuint vertexShader = compileShader ( GL_VERTEX_SHADER , vertexSources , vertexSourceLengths , vertexSourceCount ) ;
2018-08-07 23:58:14 +00:00
// Fragment
2021-02-19 21:07:27 +00:00
const char * fragmentSources [ ] = { version , computeExtensions , singlepass [ 1 ] , flagSource ? flagSource : " " , lovrShaderFragmentPrefix , fragmentSource , lovrShaderFragmentSuffix } ;
int fragmentSourceLengths [ ] = { - 1 , - 1 , - 1 , - 1 , - 1 , fragmentSourceLength , - 1 } ;
2020-10-26 08:03:19 +00:00
int fragmentSourceCount = sizeof ( fragmentSources ) / sizeof ( fragmentSources [ 0 ] ) ;
2020-01-27 21:07:41 +00:00
GLuint fragmentShader = compileShader ( GL_FRAGMENT_SHADER , fragmentSources , fragmentSourceLengths , fragmentSourceCount ) ;
2018-08-07 23:58:14 +00:00
2019-05-14 10:48:01 +00:00
free ( flagSource ) ;
2018-08-07 23:58:14 +00:00
// Link
uint32_t program = glCreateProgram ( ) ;
glAttachShader ( program , vertexShader ) ;
glAttachShader ( program , fragmentShader ) ;
glBindAttribLocation ( program , LOVR_SHADER_POSITION , " lovrPosition " ) ;
glBindAttribLocation ( program , LOVR_SHADER_NORMAL , " lovrNormal " ) ;
glBindAttribLocation ( program , LOVR_SHADER_TEX_COORD , " lovrTexCoord " ) ;
glBindAttribLocation ( program , LOVR_SHADER_VERTEX_COLOR , " lovrVertexColor " ) ;
glBindAttribLocation ( program , LOVR_SHADER_TANGENT , " lovrTangent " ) ;
glBindAttribLocation ( program , LOVR_SHADER_BONES , " lovrBones " ) ;
glBindAttribLocation ( program , LOVR_SHADER_BONE_WEIGHTS , " lovrBoneWeights " ) ;
2018-12-27 21:50:42 +00:00
glBindAttribLocation ( program , LOVR_SHADER_DRAW_ID , " lovrDrawID " ) ;
2018-08-07 23:58:14 +00:00
linkProgram ( program ) ;
glDetachShader ( program , vertexShader ) ;
glDeleteShader ( vertexShader ) ;
glDetachShader ( program , fragmentShader ) ;
glDeleteShader ( fragmentShader ) ;
shader - > program = program ;
shader - > type = SHADER_GRAPHICS ;
2018-12-25 05:08:06 +00:00
// Generic attributes
2018-08-07 23:58:14 +00:00
lovrGpuUseProgram ( program ) ;
glVertexAttrib4fv ( LOVR_SHADER_VERTEX_COLOR , ( float [ 4 ] ) { 1. , 1. , 1. , 1. } ) ;
2019-02-14 17:45:29 +00:00
glVertexAttribI4uiv ( LOVR_SHADER_BONES , ( uint32_t [ 4 ] ) { 0. , 0. , 0. , 0. } ) ;
2018-08-07 23:58:14 +00:00
glVertexAttrib4fv ( LOVR_SHADER_BONE_WEIGHTS , ( float [ 4 ] ) { 1. , 0. , 0. , 0. } ) ;
2019-07-02 19:51:36 +00:00
glVertexAttribI4ui ( LOVR_SHADER_DRAW_ID , 0 , 0 , 0 , 0 ) ;
2018-08-07 23:58:14 +00:00
lovrShaderSetupUniforms ( shader ) ;
2018-07-17 06:51:11 +00:00
2018-07-17 10:35:01 +00:00
// Attribute cache
int32_t attributeCount ;
glGetProgramiv ( program , GL_ACTIVE_ATTRIBUTES , & attributeCount ) ;
2020-05-21 06:24:19 +00:00
map_init ( & shader - > attributes , attributeCount ) ;
2018-07-17 10:35:01 +00:00
for ( int i = 0 ; i < attributeCount ; i + + ) {
char name [ LOVR_MAX_ATTRIBUTE_LENGTH ] ;
2018-12-27 21:50:42 +00:00
GLint size ;
GLenum type ;
2019-09-07 22:07:07 +00:00
GLsizei length ;
glGetActiveAttrib ( program , i , LOVR_MAX_ATTRIBUTE_LENGTH , & length , & size , & type , name ) ;
2020-07-30 08:46:17 +00:00
int location = glGetAttribLocation ( program , name ) ;
if ( location > = 0 ) {
map_set ( & shader - > attributes , hash64 ( name , length ) , ( location < < 1 ) | isAttributeTypeInteger ( type ) ) ;
}
2018-07-17 10:35:01 +00:00
}
2018-07-17 06:51:11 +00:00
2019-06-25 08:06:55 +00:00
shader - > multiview = multiview ;
2018-07-17 10:35:01 +00:00
return shader ;
2018-07-17 06:51:11 +00:00
}
2020-02-23 06:42:05 +00:00
Shader * lovrShaderCreateDefault ( DefaultShader type , ShaderFlag * flags , uint32_t flagCount , bool multiview ) {
switch ( type ) {
case SHADER_UNLIT : return lovrShaderCreateGraphics ( NULL , - 1 , NULL , - 1 , flags , flagCount , multiview ) ;
case SHADER_STANDARD : return lovrShaderCreateGraphics ( lovrStandardVertexShader , - 1 , lovrStandardFragmentShader , - 1 , flags , flagCount , multiview ) ;
case SHADER_CUBE : return lovrShaderCreateGraphics ( lovrCubeVertexShader , - 1 , lovrCubeFragmentShader , - 1 , flags , flagCount , multiview ) ;
case SHADER_PANO : return lovrShaderCreateGraphics ( lovrCubeVertexShader , - 1 , lovrPanoFragmentShader , - 1 , flags , flagCount , multiview ) ;
case SHADER_FONT : return lovrShaderCreateGraphics ( NULL , - 1 , lovrFontFragmentShader , - 1 , flags , flagCount , multiview ) ;
case SHADER_FILL : return lovrShaderCreateGraphics ( lovrFillVertexShader , - 1 , NULL , - 1 , flags , flagCount , multiview ) ;
default : lovrThrow ( " Unknown default shader type " ) ; return NULL ;
}
}
Shader * lovrShaderCreateCompute ( const char * source , int length , ShaderFlag * flags , uint32_t flagCount ) {
2021-02-09 00:23:18 +00:00
Shader * shader = calloc ( 1 , sizeof ( Shader ) ) ;
lovrAssert ( shader , " Out of memory " ) ;
shader - > ref = 1 ;
2019-01-29 23:05:23 +00:00
# ifdef LOVR_WEBGL
2018-08-09 02:37:58 +00:00
lovrThrow ( " Compute shaders are not supported on this system " ) ;
# else
2020-01-30 02:22:16 +00:00
lovrAssert ( state . features . compute , " Compute shaders are not supported on this system " ) ;
2019-05-14 10:48:01 +00:00
char * flagSource = lovrShaderGetFlagCode ( flags , flagCount ) ;
const char * sources [ ] = { lovrShaderComputePrefix , flagSource ? flagSource : " " , source , lovrShaderComputeSuffix } ;
2020-01-27 21:07:41 +00:00
int lengths [ ] = { - 1 , - 1 , length , - 1 } ;
2020-10-26 08:03:19 +00:00
int count = sizeof ( sources ) / sizeof ( sources [ 0 ] ) ;
2020-01-27 21:07:41 +00:00
GLuint computeShader = compileShader ( GL_COMPUTE_SHADER , sources , lengths , count ) ;
2019-05-14 10:48:01 +00:00
free ( flagSource ) ;
2018-08-07 23:58:14 +00:00
GLuint program = glCreateProgram ( ) ;
glAttachShader ( program , computeShader ) ;
linkProgram ( program ) ;
glDetachShader ( program , computeShader ) ;
glDeleteShader ( computeShader ) ;
shader - > program = program ;
shader - > type = SHADER_COMPUTE ;
lovrShaderSetupUniforms ( shader ) ;
2018-08-09 02:37:58 +00:00
# endif
2018-08-07 23:58:14 +00:00
return shader ;
}
2018-07-17 10:35:01 +00:00
void lovrShaderDestroy ( void * ref ) {
Shader * shader = ref ;
2019-01-04 09:35:29 +00:00
lovrGraphicsFlushShader ( shader ) ;
2018-07-17 10:35:01 +00:00
glDeleteProgram ( shader - > program ) ;
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > uniforms . length ; i + + ) {
2018-07-28 22:40:13 +00:00
free ( shader - > uniforms . data [ i ] . value . data ) ;
2018-08-02 10:26:03 +00:00
}
2019-01-16 16:52:21 +00:00
for ( BlockType type = BLOCK_UNIFORM ; type < = BLOCK_COMPUTE ; type + + ) {
2019-06-08 10:45:03 +00:00
for ( size_t i = 0 ; i < shader - > blocks [ type ] . length ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( shader - > blocks [ type ] . data [ i ] . source , lovrBufferDestroy ) ;
2020-05-23 19:03:16 +00:00
arr_free ( & shader - > blocks [ type ] . data [ i ] . uniforms ) ;
2018-08-03 19:28:25 +00:00
}
2018-08-02 10:26:03 +00:00
}
2019-06-08 10:45:03 +00:00
arr_free ( & shader - > uniforms ) ;
arr_free ( & shader - > blocks [ BLOCK_UNIFORM ] ) ;
arr_free ( & shader - > blocks [ BLOCK_COMPUTE ] ) ;
2019-09-07 22:07:07 +00:00
map_free ( & shader - > attributes ) ;
map_free ( & shader - > uniformMap ) ;
map_free ( & shader - > blockMap ) ;
2021-02-09 00:52:26 +00:00
free ( shader ) ;
2018-07-17 06:51:11 +00:00
}
2020-02-23 06:42:05 +00:00
ShaderType lovrShaderGetType ( Shader * shader ) {
return shader - > type ;
}
2020-05-21 06:24:19 +00:00
int lovrShaderGetAttributeLocation ( Shader * shader , const char * name , bool * integer ) {
uint64_t info = map_get ( & shader - > attributes , hash64 ( name , strlen ( name ) ) ) ;
* integer = info & 1 ;
return info = = MAP_NIL ? - 1 : ( int ) ( info > > 1 ) ;
2020-02-23 06:42:05 +00:00
}
bool lovrShaderHasUniform ( Shader * shader , const char * name ) {
return map_get ( & shader - > uniformMap , hash64 ( name , strlen ( name ) ) ) ! = MAP_NIL ;
}
bool lovrShaderHasBlock ( Shader * shader , const char * name ) {
return map_get ( & shader - > blockMap , hash64 ( name , strlen ( name ) ) ) ! = MAP_NIL ;
}
const Uniform * lovrShaderGetUniform ( Shader * shader , const char * name ) {
uint64_t index = map_get ( & shader - > uniformMap , hash64 ( name , strlen ( name ) ) ) ;
return index = = MAP_NIL ? NULL : & shader - > uniforms . data [ index ] ;
}
static void lovrShaderSetUniform ( Shader * shader , const char * name , UniformType type , void * data , int start , int count , int size , const char * debug ) {
uint64_t index = map_get ( & shader - > uniformMap , hash64 ( name , strlen ( name ) ) ) ;
if ( index = = MAP_NIL ) {
return ;
}
Uniform * uniform = & shader - > uniforms . data [ index ] ;
lovrAssert ( uniform - > type = = type , " Unable to send %ss to uniform %s " , debug , name ) ;
lovrAssert ( ( start + count ) * size < = uniform - > size , " Too many %ss for uniform %s, maximum is %d " , debug , name , uniform - > size / size ) ;
void * dest = uniform - > value . bytes + start * size ;
if ( memcmp ( dest , data , count * size ) ) {
lovrGraphicsFlushShader ( shader ) ;
memcpy ( dest , data , count * size ) ;
uniform - > dirty = true ;
}
}
void lovrShaderSetFloats ( Shader * shader , const char * name , float * data , int start , int count ) {
lovrShaderSetUniform ( shader , name , UNIFORM_FLOAT , data , start , count , sizeof ( float ) , " float " ) ;
}
void lovrShaderSetInts ( Shader * shader , const char * name , int * data , int start , int count ) {
lovrShaderSetUniform ( shader , name , UNIFORM_INT , data , start , count , sizeof ( int ) , " int " ) ;
}
void lovrShaderSetMatrices ( Shader * shader , const char * name , float * data , int start , int count ) {
lovrShaderSetUniform ( shader , name , UNIFORM_MATRIX , data , start , count , sizeof ( float ) , " float " ) ;
}
void lovrShaderSetTextures ( Shader * shader , const char * name , Texture * * data , int start , int count ) {
lovrShaderSetUniform ( shader , name , UNIFORM_SAMPLER , data , start , count , sizeof ( Texture * ) , " texture " ) ;
}
2021-02-09 03:17:47 +00:00
void lovrShaderSetImages ( Shader * shader , const char * name , StorageImage * data , int start , int count ) {
lovrShaderSetUniform ( shader , name , UNIFORM_IMAGE , data , start , count , sizeof ( StorageImage ) , " image " ) ;
2020-02-23 06:42:05 +00:00
}
void lovrShaderSetColor ( Shader * shader , const char * name , Color color ) {
color . r = lovrMathGammaToLinear ( color . r ) ;
color . g = lovrMathGammaToLinear ( color . g ) ;
color . b = lovrMathGammaToLinear ( color . b ) ;
lovrShaderSetUniform ( shader , name , UNIFORM_FLOAT , ( float * ) & color , 0 , 4 , sizeof ( float ) , " float " ) ;
}
void lovrShaderSetBlock ( Shader * shader , const char * name , Buffer * buffer , size_t offset , size_t size , UniformAccess access ) {
uint64_t id = map_get ( & shader - > blockMap , hash64 ( name , strlen ( name ) ) ) ;
if ( id = = MAP_NIL ) return ;
int type = id & 1 ;
int index = id > > 1 ;
UniformBlock * block = & shader - > blocks [ type ] . data [ index ] ;
if ( block - > source ! = buffer | | block - > offset ! = offset | | block - > size ! = size ) {
lovrGraphicsFlushShader ( shader ) ;
lovrRetain ( buffer ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( block - > source , lovrBufferDestroy ) ;
2020-02-23 06:42:05 +00:00
block - > access = access ;
block - > source = buffer ;
block - > offset = offset ;
block - > size = size ;
}
}
// ShaderBlock
// Calculates uniform size and byte offsets using std140 rules, returning the total buffer size
size_t lovrShaderComputeUniformLayout ( arr_uniform_t * uniforms ) {
size_t size = 0 ;
for ( size_t i = 0 ; i < uniforms - > length ; i + + ) {
int align ;
Uniform * uniform = & uniforms - > data [ i ] ;
if ( uniform - > count > 1 | | uniform - > type = = UNIFORM_MATRIX ) {
align = 16 ;
uniform - > size = align * uniform - > count * ( uniform - > type = = UNIFORM_MATRIX ? uniform - > components : 1 ) ;
} else {
align = ( uniform - > components + ( uniform - > components = = 3 ) ) * 4 ;
uniform - > size = uniform - > components * 4 ;
}
uniform - > offset = ( size + ( align - 1 ) ) & - align ;
size = uniform - > offset + uniform - > size ;
}
return size ;
}
ShaderBlock * lovrShaderBlockCreate ( BlockType type , Buffer * buffer , arr_uniform_t * uniforms ) {
2021-02-09 00:23:18 +00:00
ShaderBlock * block = calloc ( 1 , sizeof ( ShaderBlock ) ) ;
lovrAssert ( block , " Out of memory " ) ;
block - > ref = 1 ;
2022-03-14 19:27:58 +00:00
arr_init ( & block - > uniforms , arr_alloc ) ;
2020-10-26 08:03:19 +00:00
map_init ( & block - > uniformMap , ( uint32_t ) uniforms - > length ) ;
2020-02-23 06:42:05 +00:00
arr_append ( & block - > uniforms , uniforms - > data , uniforms - > length ) ;
for ( size_t i = 0 ; i < block - > uniforms . length ; i + + ) {
Uniform * uniform = & block - > uniforms . data [ i ] ;
map_set ( & block - > uniformMap , hash64 ( uniform - > name , strlen ( uniform - > name ) ) , i ) ;
}
block - > type = type ;
block - > buffer = buffer ;
lovrRetain ( buffer ) ;
return block ;
}
void lovrShaderBlockDestroy ( void * ref ) {
ShaderBlock * block = ref ;
2021-02-09 00:52:26 +00:00
lovrRelease ( block - > buffer , lovrBufferDestroy ) ;
2020-02-23 06:42:05 +00:00
arr_free ( & block - > uniforms ) ;
map_free ( & block - > uniformMap ) ;
2021-02-09 00:52:26 +00:00
free ( block ) ;
2020-02-23 06:42:05 +00:00
}
BlockType lovrShaderBlockGetType ( ShaderBlock * block ) {
return block - > type ;
}
2020-07-04 23:50:00 +00:00
char * lovrShaderBlockGetShaderCode ( ShaderBlock * block , const char * blockName , const char * namespace , size_t * length ) {
2020-02-23 06:42:05 +00:00
// Calculate
size_t size = 0 ;
size_t tab = 2 ;
size + = 15 ; // "layout(std140) "
size + = block - > type = = BLOCK_UNIFORM ? 7 : 6 ; // "uniform" || "buffer"
size + = 1 ; // " "
size + = strlen ( blockName ) ;
size + = 3 ; // " {\n"
for ( size_t i = 0 ; i < block - > uniforms . length ; i + + ) {
size + = tab ;
size + = getUniformTypeLength ( & block - > uniforms . data [ i ] ) ;
size + = 1 ; // " "
size + = strlen ( block - > uniforms . data [ i ] . name ) ;
size + = 2 ; // ";\n"
}
2020-07-04 23:50:00 +00:00
if ( namespace ) {
size + = 2 ; // "} "
size + = strlen ( namespace ) ;
size + = 2 ; // ";\n"
} else {
size + = 3 ; // "};\n"
}
2020-02-23 06:42:05 +00:00
// Allocate
char * code = malloc ( size + 1 ) ;
lovrAssert ( code , " Out of memory " ) ;
// Concatenate
char * s = code ;
s + = sprintf ( s , " layout(std140) %s %s { \n " , block - > type = = BLOCK_UNIFORM ? " uniform " : " buffer " , blockName ) ;
for ( size_t i = 0 ; i < block - > uniforms . length ; i + + ) {
const Uniform * uniform = & block - > uniforms . data [ i ] ;
if ( uniform - > count > 1 ) {
s + = sprintf ( s , " %s %s[%d]; \n " , getUniformTypeName ( uniform ) , uniform - > name , uniform - > count ) ;
} else {
s + = sprintf ( s , " %s %s; \n " , getUniformTypeName ( uniform ) , uniform - > name ) ;
}
}
2020-07-04 23:50:00 +00:00
if ( namespace ) {
s + = sprintf ( s , " } %s; \n " , namespace ) ;
} else {
s + = sprintf ( s , " }; \n " ) ;
}
2020-02-23 06:42:05 +00:00
* s = ' \0 ' ;
* length = size ;
return code ;
}
const Uniform * lovrShaderBlockGetUniform ( ShaderBlock * block , const char * name ) {
uint64_t index = map_get ( & block - > uniformMap , hash64 ( name , strlen ( name ) ) ) ;
return index = = MAP_NIL ? NULL : & block - > uniforms . data [ index ] ;
}
Buffer * lovrShaderBlockGetBuffer ( ShaderBlock * block ) {
return block - > buffer ;
}
2018-07-17 10:35:01 +00:00
// Mesh
2018-07-17 06:51:11 +00:00
2020-02-23 07:18:54 +00:00
Mesh * lovrMeshCreate ( DrawMode mode , Buffer * vertexBuffer , uint32_t vertexCount ) {
2021-02-09 00:23:18 +00:00
Mesh * mesh = calloc ( 1 , sizeof ( Mesh ) ) ;
lovrAssert ( mesh , " Out of memory " ) ;
mesh - > ref = 1 ;
2018-12-13 02:43:04 +00:00
mesh - > mode = mode ;
2018-12-27 21:50:42 +00:00
mesh - > vertexBuffer = vertexBuffer ;
2019-01-04 03:43:35 +00:00
mesh - > vertexCount = vertexCount ;
lovrRetain ( mesh - > vertexBuffer ) ;
2018-07-17 10:35:01 +00:00
glGenVertexArrays ( 1 , & mesh - > vao ) ;
2019-09-07 22:07:07 +00:00
map_init ( & mesh - > attributeMap , MAX_ATTRIBUTES ) ;
2019-01-27 22:29:06 +00:00
memset ( mesh - > locations , 0xff , MAX_ATTRIBUTES * sizeof ( uint8_t ) ) ;
2018-09-26 00:10:09 +00:00
return mesh ;
}
2018-07-17 10:35:01 +00:00
void lovrMeshDestroy ( void * ref ) {
Mesh * mesh = ref ;
2019-01-04 09:35:29 +00:00
lovrGraphicsFlushMesh ( mesh ) ;
2018-07-17 10:35:01 +00:00
glDeleteVertexArrays ( 1 , & mesh - > vao ) ;
2019-05-21 03:35:07 +00:00
for ( uint32_t i = 0 ; i < mesh - > attributeCount ; i + + ) {
2021-02-09 00:52:26 +00:00
lovrRelease ( mesh - > attributes [ i ] . buffer , lovrBufferDestroy ) ;
2018-07-17 06:51:11 +00:00
}
2019-09-07 22:07:07 +00:00
map_free ( & mesh - > attributeMap ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( mesh - > vertexBuffer , lovrBufferDestroy ) ;
lovrRelease ( mesh - > indexBuffer , lovrBufferDestroy ) ;
lovrRelease ( mesh - > material , lovrMaterialDestroy ) ;
free ( mesh ) ;
2018-07-17 10:35:01 +00:00
}
2019-01-04 03:43:35 +00:00
2019-01-17 22:14:13 +00:00
void lovrMeshSetIndexBuffer ( Mesh * mesh , Buffer * buffer , uint32_t indexCount , size_t indexSize , size_t offset ) {
2019-01-04 03:43:35 +00:00
if ( mesh - > indexBuffer ! = buffer | | mesh - > indexCount ! = indexCount | | mesh - > indexSize ! = indexSize ) {
lovrGraphicsFlushMesh ( mesh ) ;
lovrRetain ( buffer ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( mesh - > indexBuffer , lovrBufferDestroy ) ;
2019-01-04 03:43:35 +00:00
mesh - > indexBuffer = buffer ;
mesh - > indexCount = indexCount ;
mesh - > indexSize = indexSize ;
2019-01-17 22:14:13 +00:00
mesh - > indexOffset = offset ;
2019-01-04 03:43:35 +00:00
}
}
2020-02-23 07:18:54 +00:00
Buffer * lovrMeshGetVertexBuffer ( Mesh * mesh ) {
return mesh - > vertexBuffer ;
}
Buffer * lovrMeshGetIndexBuffer ( Mesh * mesh ) {
return mesh - > indexBuffer ;
}
uint32_t lovrMeshGetVertexCount ( Mesh * mesh ) {
return mesh - > vertexCount ;
}
uint32_t lovrMeshGetIndexCount ( Mesh * mesh ) {
return mesh - > indexCount ;
}
size_t lovrMeshGetIndexSize ( Mesh * mesh ) {
return mesh - > indexSize ;
}
uint32_t lovrMeshGetAttributeCount ( Mesh * mesh ) {
return mesh - > attributeCount ;
}
void lovrMeshAttachAttribute ( Mesh * mesh , const char * name , MeshAttribute * attribute ) {
uint64_t hash = hash64 ( name , strlen ( name ) ) ;
lovrAssert ( map_get ( & mesh - > attributeMap , hash ) = = MAP_NIL , " Mesh already has an attribute named '%s' " , name ) ;
lovrAssert ( mesh - > attributeCount < MAX_ATTRIBUTES , " Mesh already has the max number of attributes (%d) " , MAX_ATTRIBUTES ) ;
lovrAssert ( strlen ( name ) < MAX_ATTRIBUTE_NAME_LENGTH , " Mesh attribute name '%s' is too long (max is %d) " , name , MAX_ATTRIBUTE_NAME_LENGTH ) ;
lovrGraphicsFlushMesh ( mesh ) ;
uint64_t index = mesh - > attributeCount + + ;
mesh - > attributes [ index ] = * attribute ;
strcpy ( mesh - > attributeNames [ index ] , name ) ;
map_set ( & mesh - > attributeMap , hash , index ) ;
lovrRetain ( attribute - > buffer ) ;
}
void lovrMeshDetachAttribute ( Mesh * mesh , const char * name ) {
uint64_t hash = hash64 ( name , strlen ( name ) ) ;
uint64_t index = map_get ( & mesh - > attributeMap , hash ) ;
lovrAssert ( index ! = MAP_NIL , " No attached attribute named '%s' was found " , name ) ;
MeshAttribute * attribute = & mesh - > attributes [ index ] ;
lovrGraphicsFlushMesh ( mesh ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( attribute - > buffer , lovrBufferDestroy ) ;
2020-02-23 07:18:54 +00:00
map_remove ( & mesh - > attributeMap , hash ) ;
mesh - > attributeNames [ index ] [ 0 ] = ' \0 ' ;
memmove ( mesh - > attributeNames + index , mesh - > attributeNames + index + 1 , ( mesh - > attributeCount - index - 1 ) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof ( char ) ) ;
memmove ( mesh - > attributes + index , mesh - > attributes + index + 1 , ( mesh - > attributeCount - index - 1 ) * sizeof ( MeshAttribute ) ) ;
mesh - > attributeCount - - ;
for ( uint32_t i = 0 ; i < MAX_ATTRIBUTES ; i + + ) {
if ( mesh - > locations [ i ] > index ) {
mesh - > locations [ i ] - - ;
} else if ( mesh - > locations [ i ] = = index ) {
mesh - > locations [ i ] = 0xff ;
}
}
}
const MeshAttribute * lovrMeshGetAttribute ( Mesh * mesh , uint32_t index ) {
return index < mesh - > attributeCount ? & mesh - > attributes [ index ] : NULL ;
}
uint32_t lovrMeshGetAttributeIndex ( Mesh * mesh , const char * name ) {
uint64_t hash = hash64 ( name , strlen ( name ) ) ;
uint64_t index = map_get ( & mesh - > attributeMap , hash ) ;
return index = = MAP_NIL ? ~ 0u : index ;
}
const char * lovrMeshGetAttributeName ( Mesh * mesh , uint32_t index ) {
return mesh - > attributeNames [ index ] ;
}
bool lovrMeshIsAttributeEnabled ( Mesh * mesh , const char * name ) {
uint64_t hash = hash64 ( name , strlen ( name ) ) ;
uint64_t index = map_get ( & mesh - > attributeMap , hash ) ;
lovrAssert ( index ! = MAP_NIL , " Mesh does not have an attribute named '%s' " , name ) ;
return ! mesh - > attributes [ index ] . disabled ;
}
void lovrMeshSetAttributeEnabled ( Mesh * mesh , const char * name , bool enable ) {
bool disable = ! enable ;
uint64_t hash = hash64 ( name , strlen ( name ) ) ;
uint64_t index = map_get ( & mesh - > attributeMap , hash ) ;
lovrAssert ( index ! = MAP_NIL , " Mesh does not have an attribute named '%s' " , name ) ;
if ( mesh - > attributes [ index ] . disabled ! = disable ) {
lovrGraphicsFlushMesh ( mesh ) ;
mesh - > attributes [ index ] . disabled = disable ;
}
}
DrawMode lovrMeshGetDrawMode ( Mesh * mesh ) {
return mesh - > mode ;
}
void lovrMeshSetDrawMode ( Mesh * mesh , DrawMode mode ) {
mesh - > mode = mode ;
}
void lovrMeshGetDrawRange ( Mesh * mesh , uint32_t * start , uint32_t * count ) {
* start = mesh - > drawStart ;
* count = mesh - > drawCount ;
}
void lovrMeshSetDrawRange ( Mesh * mesh , uint32_t start , uint32_t count ) {
uint32_t limit = mesh - > indexSize > 0 ? mesh - > indexCount : mesh - > vertexCount ;
lovrAssert ( start + count < = limit , " Invalid mesh draw range [%d, %d] " , start + 1 , start + count + 1 ) ;
mesh - > drawStart = start ;
mesh - > drawCount = count ;
}
Material * lovrMeshGetMaterial ( Mesh * mesh ) {
return mesh - > material ;
}
void lovrMeshSetMaterial ( Mesh * mesh , Material * material ) {
lovrRetain ( material ) ;
2021-02-09 00:52:26 +00:00
lovrRelease ( mesh - > material , lovrMaterialDestroy ) ;
2020-02-23 07:18:54 +00:00
mesh - > material = material ;
}