2018-12-19 07:53:51 +00:00
# include "graphics/opengl.h"
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"
# include "data/modelData.h"
2019-09-07 22:07:07 +00:00
# include "core/hash.h"
2019-06-02 07:20:10 +00:00
# include "core/ref.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
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
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 ] ;
2018-08-18 02:52:34 +00:00
Image 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 ;
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 ;
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 ;
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 ;
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 ;
2018-08-16 21:28:10 +00:00
case FORMAT_DXT1 :
case FORMAT_DXT3 :
case FORMAT_DXT5 :
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 :
2019-02-17 07:43:20 +00:00
default :
2018-08-16 21:28:10 +00:00
lovrThrow ( " Unreachable " ) ;
return GL_UNSIGNED_BYTE ;
}
}
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
}
}
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
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 ;
}
}
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 ) ;
}
}
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 ) ;
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , state . textures [ slot ] ) ;
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
2018-08-18 02:52:34 +00:00
static void lovrGpuBindImage ( Image * image , int slot ) {
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
if ( memcmp ( state . images + slot , image , sizeof ( Image ) ) ) {
2019-06-20 18:35:46 +00:00
Texture * texture = image - > texture ? image - > texture : state . defaultTexture ;
2018-08-11 07:02:30 +00:00
lovrAssert ( ! texture - > srgb , " sRGB textures can not be used as image uniforms " ) ;
2018-08-11 06:54:46 +00:00
lovrAssert ( ! isTextureFormatCompressed ( texture - > format ) , " Compressed textures can not be used as image uniforms " ) ;
lovrAssert ( texture - > format ! = FORMAT_RGB & & texture - > format ! = FORMAT_RGBA4 & & texture - > format ! = FORMAT_RGB5A1 , " Unsupported texture format for image uniform " ) ;
2019-05-20 21:34:03 +00:00
lovrAssert ( image - > mipmap < ( int ) texture - > mipmapCount , " Invalid mipmap level '%d' for image uniform " , image - > mipmap ) ;
lovrAssert ( image - > slice < ( int ) texture - > depth , " Invalid texture slice '%d' for image uniform " , image - > slice ) ;
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 ) ;
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , state . images [ slot ] . texture ) ;
2018-09-04 13:58:54 +00:00
glBindImageTexture ( slot , texture - > id , image - > mipmap , layered , slice , glAccess , glFormat ) ;
2018-08-18 02:52:34 +00:00
memcpy ( state . images + slot , image , sizeof ( Image ) ) ;
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 ;
2018-10-25 23:15:41 +00:00
2019-01-27 22:29:06 +00:00
if ( ( attribute = & mesh - > attributes [ i ] ) - > disabled ) { continue ; }
if ( ( location = lovrShaderGetAttributeLocation ( shader , mesh - > attributeNames [ i ] ) ) < 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
2019-01-27 22:29:06 +00:00
if ( attribute - > integer ) {
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
2018-07-17 10:35:01 +00:00
// Depth test
if ( state . depthTest ! = pipeline - > depthTest ) {
state . depthTest = pipeline - > depthTest ;
if ( state . depthTest ! = COMPARE_NONE ) {
if ( ! state . depthEnabled ) {
state . depthEnabled = true ;
glEnable ( GL_DEPTH_TEST ) ;
}
glDepthFunc ( convertCompareMode ( state . depthTest ) ) ;
} else if ( state . depthEnabled ) {
state . depthEnabled = false ;
glDisable ( GL_DEPTH_TEST ) ;
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-07-17 10:35:01 +00:00
// Depth write
2018-12-27 21:50:42 +00:00
if ( state . depthWrite ! = ( pipeline - > depthWrite & & ! state . stencilWriting ) ) {
state . depthWrite = pipeline - > depthWrite & & ! state . stencilWriting ;
2018-07-17 10:35:01 +00:00
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 + + ) {
Image * 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-01-23 19:18:04 +00:00
lovrGpuBindImage ( image , uniform - > baseSlot + j ) ;
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
2019-12-10 21:15:12 +00:00
void lovrGpuInit ( void * ( * getProcAddress ) ( const char * ) ) {
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
# ifndef LOVR_WEBGL
2019-06-10 06:59:57 +00:00
state . features . astc = GLAD_GL_ES_VERSION_3_2 ;
2019-01-16 16:52:21 +00:00
state . features . compute = GLAD_GL_ARB_compute_shader ;
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 ;
state . features . multiview = GLAD_GL_OVR_multiview2 & & GLAD_GL_OVR_multiview_multisampled_render_to_texture ;
2019-05-26 11:37:00 +00:00
state . features . timers = GLAD_GL_VERSION_3_3 | | GLAD_GL_EXT_disjoint_timer_query ;
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
# ifdef LOVR_GL
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
if ( state . features . multiview & & GLAD_GL_ES_VERSION_3_0 ) {
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 + + ) {
2019-06-08 10:45:03 +00:00
arr_init ( & state . incoherents [ i ] ) ;
2018-12-25 05:46:50 +00:00
}
2019-06-20 18:35:46 +00:00
TextureData * textureData = lovrTextureDataCreate ( 1 , 1 , 0xff , FORMAT_RGBA ) ;
state . defaultTexture = lovrTextureCreate ( TEXTURE_2D , & textureData , 1 , true , false , 0 ) ;
lovrTextureSetFilter ( state . defaultTexture , ( TextureFilter ) { . mode = FILTER_NEAREST } ) ;
lovrTextureSetWrap ( state . defaultTexture , ( TextureWrap ) { WRAP_CLAMP , WRAP_CLAMP , WRAP_CLAMP } ) ;
lovrRelease ( TextureData , textureData ) ;
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 ( ) {
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , state . defaultTexture ) ;
2018-12-25 05:46:50 +00:00
for ( int i = 0 ; i < MAX_TEXTURES ; i + + ) {
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , state . textures [ i ] ) ;
2018-12-25 05:46:50 +00:00
}
for ( int i = 0 ; i < MAX_IMAGES ; i + + ) {
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , state . images [ i ] . texture ) ;
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
lovrAssert ( GLAD_GL_ARB_compute_shader , " Compute shaders are not supported on this system " ) ;
lovrAssert ( shader - > type = = SHADER_COMPUTE , " Attempt to use a non-compute shader for a compute operation " ) ;
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 ( ) {
2019-09-19 01:21:38 +00:00
memset ( & state . stats , 0 , sizeof ( state . stats ) ) ;
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 ( ) ;
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
}
2018-09-01 06:24:59 +00:00
void lovrGpuDirtyTexture ( ) {
2019-04-08 12:19:12 +00:00
lovrRelease ( Texture , state . textures [ state . activeTexture ] ) ;
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 ) {
2019-07-29 00:28:07 +00:00
# ifndef LOVR_WEBGL
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 ) {
2019-07-29 00:28:07 +00:00
# ifndef LOVR_WEBGL
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
2019-05-20 21:34:03 +00:00
Texture * lovrTextureInit ( Texture * texture , TextureType type , TextureData * * slices , uint32_t sliceCount , bool srgb , bool mipmaps , uint32_t msaa ) {
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 ;
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 } ) ;
2018-08-29 11:28:12 +00:00
if ( msaa > 0 ) {
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 ;
}
2019-06-26 01:56:59 +00:00
Texture * lovrTextureInitFromHandle ( Texture * texture , uint32_t handle , TextureType type , uint32_t depth ) {
2018-09-09 05:28:30 +00:00
texture - > type = type ;
texture - > id = handle ;
texture - > target = convertTextureTarget ( type ) ;
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
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 ;
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 ) ;
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 " ) ;
lovrAssert ( texture - > type ! = TEXTURE_CUBE | | depth = = 6 , " 6 images are required for a cube texture \n " ) ;
lovrAssert ( texture - > type ! = TEXTURE_2D | | depth = = 1 , " 2D textures can only contain a single image " ) ;
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
2018-08-09 23:12:57 +00:00
GLenum glFormat = convertTextureFormat ( format ) ;
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
if ( texture - > type = = TEXTURE_ARRAY ) {
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 {
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 ) ;
}
2018-07-17 06:51:11 +00:00
}
2019-05-20 21:34:03 +00:00
void lovrTextureReplacePixels ( Texture * texture , TextureData * textureData , 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 ) ;
uint32_t width = textureData - > width ;
uint32_t height = textureData - > 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 ) ;
2018-07-17 10:35:01 +00:00
GLenum glFormat = convertTextureFormat ( textureData - > format ) ;
GLenum glInternalFormat = convertTextureFormatInternal ( textureData - > 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 ) ;
2018-07-17 10:35:01 +00:00
if ( isTextureFormatCompressed ( textureData - > 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 " ) ;
2019-06-08 10:45:03 +00:00
for ( uint32_t i = 0 ; i < textureData - > mipmapCount ; i + + ) {
Mipmap * m = textureData - > 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 {
2020-01-19 21:59:44 +00:00
lovrAssert ( textureData - > blob - > data , " Trying to replace Texture pixels with empty pixel data " ) ;
2018-09-01 12:40:45 +00:00
GLenum glType = convertTextureFormatType ( textureData - > format ) ;
2018-07-17 10:35:01 +00:00
switch ( texture - > type ) {
case TEXTURE_2D :
case TEXTURE_CUBE :
2020-01-19 21:59:44 +00:00
glTexSubImage2D ( binding , mipmap , x , y , width , height , glFormat , glType , textureData - > 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 :
2020-01-19 21:59:44 +00:00
glTexSubImage3D ( binding , mipmap , x , y , slice , width , height , 1 , glFormat , glType , textureData - > 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
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 ( ) ;
2019-01-25 01:00:41 +00:00
float anisotropy = filter . mode = = FILTER_ANISOTROPIC ? MAX ( filter . anisotropy , 1.f ) : 1.f ;
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 :
case FILTER_ANISOTROPIC :
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
}
2018-08-09 23:12:57 +00:00
glTexParameteri ( texture - > target , GL_TEXTURE_MAX_ANISOTROPY_EXT , anisotropy ) ;
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
2019-05-20 21:34:03 +00:00
Canvas * lovrCanvasInit ( Canvas * canvas , uint32_t width , uint32_t height , CanvasFlags flags ) {
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
2019-05-20 21:34:03 +00:00
Canvas * lovrCanvasInitFromHandle ( Canvas * canvas , uint32_t width , uint32_t height , CanvasFlags flags , uint32_t framebuffer , uint32_t depthBuffer , uint32_t resolveBuffer , uint32_t attachmentCount , bool immortal ) {
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 + + ) {
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , canvas - > attachments [ i ] . texture ) ;
2018-08-24 22:11:37 +00:00
}
2019-04-05 10:41:03 +00:00
lovrRelease ( Texture , canvas - > depth . texture ) ;
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
}
2019-05-20 21:34:03 +00:00
TextureData * lovrCanvasNewTextureData ( 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
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-08-29 21:03:14 +00:00
Texture * texture = canvas - > attachments [ index ] . texture ;
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 ) ;
}
TextureData * textureData = lovrTextureDataCreate ( canvas - > width , canvas - > height , 0x0 , FORMAT_RGBA ) ;
2020-01-19 21:59:44 +00:00
glReadPixels ( 0 , 0 , canvas - > width , canvas - > height , GL_RGBA , GL_UNSIGNED_BYTE , textureData - > blob - > data ) ;
2018-08-29 21:03:14 +00:00
if ( index ! = 0 ) {
glReadBuffer ( 0 ) ;
}
return textureData ;
}
2018-12-07 00:14:30 +00:00
// Buffer
2018-12-27 21:50:42 +00:00
Buffer * lovrBufferInit ( Buffer * buffer , size_t size , void * data , BufferType type , BufferUsage usage , bool readable ) {
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
2019-05-14 03:35:21 +00:00
# ifdef LOVR_WEBGL
buffer - > data = malloc ( size ) ;
lovrAssert ( buffer - > data , " Out of memory " ) ;
glBufferData ( glType , size , data , convertBufferUsage ( usage ) ) ;
2018-12-07 00:14:30 +00:00
2019-05-14 03:35:21 +00:00
if ( data ) {
memcpy ( buffer - > data , data , size ) ;
2018-12-07 00:14:30 +00:00
}
2019-05-14 03:52:11 +00:00
# else
if ( GLAD_GL_ARB_buffer_storage ) {
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | ( readable ? GL_MAP_READ_BIT : 0 ) ;
glBufferStorage ( glType , size , data , flags ) ;
buffer - > data = glMapBufferRange ( glType , 0 , size , flags | GL_MAP_FLUSH_EXPLICIT_BIT ) ;
} 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 ) ;
2019-05-14 03:35:21 +00:00
# ifdef LOVR_WEBGL
free ( buffer - > data ) ;
2018-12-14 23:13:21 +00:00
# endif
2019-05-14 03:35:21 +00:00
}
void * lovrBufferMap ( Buffer * buffer , size_t offset ) {
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2019-05-14 03:35:21 +00:00
if ( ! GLAD_GL_ARB_buffer_storage & & ! buffer - > mapped ) {
buffer - > mapped = true ;
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
2019-11-14 20:42:52 +00:00
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT ;
flags | = buffer - > readable ? GL_MAP_READ_BIT : GL_MAP_UNSYNCHRONIZED_BIT ;
2019-05-14 03:35:21 +00:00
buffer - > data = glMapBufferRange ( convertBufferType ( buffer - > type ) , 0 , buffer - > size , flags ) ;
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 ) {
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 ) {
# ifdef LOVR_WEBGL
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 ) ;
}
# else
2019-07-13 03:16:18 +00:00
if ( buffer - > mapped | | GLAD_GL_ARB_buffer_storage ) {
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
2019-05-19 04:34:36 +00:00
if ( buffer - > mapped ) {
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 ) {
lovrGpuBindBuffer ( buffer - > type , buffer - > id ) ;
GLenum glType = convertBufferType ( buffer - > type ) ;
# ifdef LOVR_WEBGL
glBufferData ( glType , buffer - > size , NULL , convertBufferUsage ( buffer - > usage ) ) ;
# else
2019-06-26 02:16:08 +00:00
// We unmap even if persistent mapping is supported
2019-06-30 03:08:31 +00:00
if ( buffer - > mapped | | GLAD_GL_ARB_buffer_storage ) {
glUnmapBuffer ( glType ) ;
buffer - > mapped = false ;
}
2019-06-26 02:16:08 +00:00
2019-11-14 20:42:52 +00:00
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT ;
flags | = buffer - > readable ? GL_MAP_READ_BIT : ( GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ) ;
2019-06-26 02:16:08 +00:00
flags | = GLAD_GL_ARB_buffer_storage ? GL_MAP_PERSISTENT_BIT : 0 ;
buffer - > data = glMapBufferRange ( glType , 0 , buffer - > size , flags ) ;
2019-06-25 07:09:48 +00:00
2019-06-26 02:16:08 +00:00
if ( ! GLAD_GL_ARB_buffer_storage ) {
2019-06-25 07:09:48 +00:00
buffer - > mapped = true ;
}
# endif
}
2018-07-17 10:35:01 +00:00
// Shader
2018-07-17 06:51:11 +00:00
2018-07-19 08:22:47 +00:00
static GLuint compileShader ( GLenum type , const char * * sources , int count ) {
2018-07-17 10:35:01 +00:00
GLuint shader = glCreateShader ( type ) ;
2018-07-19 08:22:47 +00:00
glShaderSource ( shader , count , sources , NULL ) ;
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 ] ;
arr_init ( uniformBlocks ) ;
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 ) ;
2019-08-22 06:16:08 +00:00
arr_init ( & uniformBlocks - > data [ uniformBlocks - > length - 1 ] . uniforms ) ;
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 ] ;
arr_init ( computeBlocks ) ;
2019-01-29 23:05:23 +00:00
# ifndef LOVR_WEBGL
2018-08-03 00:17:26 +00:00
if ( GLAD_GL_ARB_shader_storage_buffer_object & & GLAD_GL_ARB_program_interface_query ) {
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 } ;
glShaderStorageBlockBinding ( program , i , block . slot ) ;
2019-06-08 10:45:03 +00:00
arr_init ( & block . uniforms ) ;
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 ) ;
arr_init ( & shader - > uniforms ) ;
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 :
uniform . size = uniform . count * ( uniform . type = = UNIFORM_SAMPLER ? sizeof ( Texture * ) : sizeof ( Image ) ) ;
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 ;
}
2019-06-25 08:06:55 +00:00
Shader * lovrShaderInitGraphics ( Shader * shader , const char * vertexSource , const char * fragmentSource , ShaderFlag * flags , uint32_t flagCount , bool multiview ) {
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 " ;
const char * precision [ 2 ] = { " precision highp float; \n precision highp int; \n " , " precision mediump float; \n precision mediump int; \n " } ;
2018-09-01 08:57:38 +00:00
# else
2019-06-25 08:06:55 +00:00
const char * version = state . features . compute ? " #version 430 \n " : " #version 150 \n " ;
const char * precision [ 2 ] = { " " , " " } ;
2018-09-01 08:57:38 +00:00
# endif
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 ) ;
2018-08-07 23:58:14 +00:00
// Vertex
2019-06-20 21:09:55 +00:00
vertexSource = vertexSource = = NULL ? lovrUnlitVertexShader : vertexSource ;
2019-06-25 08:06:55 +00:00
const char * vertexSources [ ] = { version , singlepass [ 0 ] , precision [ 0 ] , flagSource ? flagSource : " " , lovrShaderVertexPrefix , vertexSource , lovrShaderVertexSuffix } ;
2018-08-07 23:58:14 +00:00
GLuint vertexShader = compileShader ( GL_VERTEX_SHADER , vertexSources , sizeof ( vertexSources ) / sizeof ( vertexSources [ 0 ] ) ) ;
// Fragment
2019-06-20 21:09:55 +00:00
fragmentSource = fragmentSource = = NULL ? lovrUnlitFragmentShader : fragmentSource ;
2019-06-25 08:06:55 +00:00
const char * fragmentSources [ ] = { version , singlepass [ 1 ] , precision [ 1 ] , flagSource ? flagSource : " " , lovrShaderFragmentPrefix , fragmentSource , lovrShaderFragmentSuffix } ;
2018-08-07 23:58:14 +00:00
GLuint fragmentShader = compileShader ( GL_FRAGMENT_SHADER , fragmentSources , sizeof ( fragmentSources ) / sizeof ( fragmentSources [ 0 ] ) ) ;
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 ) ;
2019-09-07 22:07:07 +00:00
map_init ( & shader - > attributes , 0 ) ;
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 ) ;
map_set ( & shader - > attributes , hash64 ( name , length ) , glGetAttribLocation ( program , name ) ) ;
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
}
2019-05-14 10:48:01 +00:00
Shader * lovrShaderInitCompute ( Shader * shader , const char * source , ShaderFlag * flags , uint32_t flagCount ) {
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
2018-08-09 01:03:46 +00:00
lovrAssert ( GLAD_GL_ARB_compute_shader , " 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 } ;
2018-08-07 23:58:14 +00:00
GLuint computeShader = compileShader ( GL_COMPUTE_SHADER , sources , sizeof ( sources ) / sizeof ( sources [ 0 ] ) ) ;
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 + + ) {
lovrRelease ( Buffer , shader - > blocks [ type ] . data [ i ] . source ) ;
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 ) ;
2018-07-17 06:51:11 +00:00
}
2018-07-17 10:35:01 +00:00
// Mesh
2018-07-17 06:51:11 +00:00
2019-01-27 22:29:06 +00:00
Mesh * lovrMeshInit ( Mesh * mesh , DrawMode mode , Buffer * vertexBuffer , uint32_t vertexCount ) {
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 + + ) {
2019-04-05 10:41:03 +00:00
lovrRelease ( Buffer , mesh - > attributes [ i ] . buffer ) ;
2018-07-17 06:51:11 +00:00
}
2019-09-07 22:07:07 +00:00
map_free ( & mesh - > attributeMap ) ;
2019-04-05 10:41:03 +00:00
lovrRelease ( Buffer , mesh - > vertexBuffer ) ;
lovrRelease ( Buffer , mesh - > indexBuffer ) ;
lovrRelease ( Material , mesh - > material ) ;
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 ) ;
2019-04-05 10:41:03 +00:00
lovrRelease ( Buffer , mesh - > indexBuffer ) ;
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
}
}