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;
|
|
|
|
bool culling;
|
|
|
|
bool depthEnabled;
|
|
|
|
CompareMode depthTest;
|
|
|
|
bool depthWrite;
|
2018-12-25 03:19:08 +00:00
|
|
|
uint8_t 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:
|
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;
|
|
|
|
#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
|
|
|
|
|
|
|
|
// Use the read framebuffer as a binding point to bind resolve textures
|
|
|
|
if (canvas->flags.msaa) {
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas->resolveBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-06-25 08:06:55 +00:00
|
|
|
glFramebufferTextureMultisampleMultiviewOVR(GL_READ_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);
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
for (int i = 0; i < uniform->count; i++) {
|
|
|
|
Texture* texture = uniform->value.textures[i];
|
|
|
|
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) {
|
|
|
|
for (int i = 0; i < uniform->count; i++) {
|
|
|
|
Texture* texture = uniform->value.images[i].texture;
|
|
|
|
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
|
2018-12-25 05:46:50 +00:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
Image* image = &uniform->value.images[i];
|
|
|
|
Texture* texture = image->texture;
|
|
|
|
lovrAssert(!texture || texture->type == uniform->textureType, "Uniform texture type mismatch for uniform %s", uniform->name);
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lovrGpuBindImage(image, uniform->baseSlot + i);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case UNIFORM_SAMPLER:
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
Texture* texture = uniform->value.textures[i];
|
|
|
|
lovrAssert(!texture || texture->type == uniform->textureType, "Uniform texture type mismatch for uniform %s", uniform->name);
|
|
|
|
lovrGpuBindTexture(texture, uniform->baseSlot + i);
|
|
|
|
}
|
|
|
|
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-05-04 00:53:33 +00:00
|
|
|
void lovrGpuInit(getProcAddressProc getProcAddress) {
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
state.lineWidth = 1;
|
|
|
|
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
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
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);
|
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);
|
2019-01-29 23:05:23 +00:00
|
|
|
#ifndef LOVR_WEBGL
|
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
|
|
|
}
|
2019-01-29 23:05:23 +00:00
|
|
|
#ifndef LOVR_WEBGL
|
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");
|
|
|
|
lovrAssert(mipmap >= 0 && 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 {
|
2018-09-01 12:40:45 +00:00
|
|
|
lovrAssert(textureData->blob.data, "Trying to replace Texture pixels with empty pixel data");
|
|
|
|
GLenum glType = convertTextureFormatType(textureData->format);
|
|
|
|
|
2018-07-17 10:35:01 +00:00
|
|
|
switch (texture->type) {
|
|
|
|
case TEXTURE_2D:
|
|
|
|
case TEXTURE_CUBE:
|
2018-08-16 21:28:10 +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:
|
2018-08-16 21:28:10 +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
|
|
|
|
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) {
|
|
|
|
canvas->depth.texture = lovrTextureCreate(TEXTURE_ARRAY, NULL, 0, false, flags.mipmaps, flags.msaa);
|
|
|
|
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
|
|
|
|
2018-12-25 05:46:50 +00:00
|
|
|
if (flags.msaa) {
|
|
|
|
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
|
|
|
|
2018-08-29 21:08:03 +00:00
|
|
|
if (canvas->flags.msaa) {
|
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);
|
|
|
|
glReadPixels(0, 0, canvas->width, canvas->height, GL_RGBA, GL_UNSIGNED_BYTE, textureData->blob.data);
|
|
|
|
|
|
|
|
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);
|
|
|
|
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT | (buffer->readable ? GL_MAP_READ_BIT : 0);
|
|
|
|
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
|
|
|
|
|
|
|
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | (buffer->readable ? GL_MAP_READ_BIT : 0);
|
|
|
|
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';
|
|
|
|
}
|
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-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);
|
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.
|
|
|
|
for (int i = 0; i < uniform.count; i++) {
|
2018-08-18 02:52:34 +00:00
|
|
|
uniform.value.ints[i] = uniform.baseSlot + i;
|
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;\nprecision highp int;\n", "precision mediump float;\nprecision 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
|
|
|
}
|
|
|
|
}
|