Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2021-01-14 20:33:45 -04:00
40 changed files with 1692 additions and 574 deletions
+2
View File
@@ -78,6 +78,8 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
mapType = BUFFERTYPE_VERTEX;
else if (typeFlags & TYPEFLAG_INDEX)
mapType = BUFFERTYPE_INDEX;
else if (typeFlags & TYPEFLAG_SHADER_STORAGE)
mapType = BUFFERTYPE_SHADER_STORAGE;
target = OpenGL::getGLBufferType(mapType);
+21 -5
View File
@@ -333,8 +333,8 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
// Set whether drawing converts input from linear -> sRGB colorspace.
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|| GLAD_ES_VERSION_3_0)
if (!gl.bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
|| GLAD_EXT_framebuffer_sRGB || GLAD_ES_VERSION_3_0))
{
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect());
@@ -362,7 +362,19 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
love::graphics::Buffer *buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
auto buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
}
if (capabilities.features[FEATURE_GLSL4] && defaultBuffers[BUFFERTYPE_SHADER_STORAGE].get() == nullptr)
{
Buffer::Settings settings(Buffer::TYPEFLAG_SHADER_STORAGE, BUFFERUSAGE_STATIC);
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT, 0}};
std::vector<float> data;
data.resize(Buffer::SHADER_STORAGE_BUFFER_MAX_STRIDE / 4);
auto buffer = newBuffer(settings, format, data.data(), data.size() * sizeof(float), data.size());
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
}
@@ -376,6 +388,9 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
if (defaultBuffers[BUFFERTYPE_TEXEL].get())
gl.setDefaultTexelBuffer((GLuint) defaultBuffers[BUFFERTYPE_TEXEL]->getTexelBufferHandle());
if (defaultBuffers[BUFFERTYPE_SHADER_STORAGE].get())
gl.setDefaultStorageBuffer((GLuint) defaultBuffers[BUFFERTYPE_SHADER_STORAGE]->getHandle());
// Reload all volatile objects.
if (!Volatile::loadAll())
::printf("Could not reload all volatile objects.\n");
@@ -1538,7 +1553,7 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.areTexelBuffersSupported();
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferTypeSupported(BUFFERTYPE_TEXEL);
static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
@@ -1547,10 +1562,11 @@ void Graphics::initCapabilities()
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = gl.getMaxShaderStorageBufferSize();
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
static_assert(LIMIT_MAX_ENUM == 9, "Graphics::initCapabilities must be updated when adding a new system limit!");
static_assert(LIMIT_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new system limit!");
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
+100 -23
View File
@@ -101,9 +101,12 @@ OpenGL::OpenGL()
, max3DTextureSize(0)
, maxCubeTextureSize(0)
, maxTextureArrayLayers(0)
, maxTexelBufferSize(0)
, maxShaderStorageBufferSize(0)
, maxRenderTargets(1)
, maxSamples(1)
, maxTextureUnits(1)
, maxShaderStorageBufferBindings(0)
, maxPointSize(1)
, coreProfile(false)
, vendor(VENDOR_UNKNOWN)
@@ -150,6 +153,16 @@ bool OpenGL::initContext()
if (strstr(device, "HD Graphics 4000") || strstr(device, "HD Graphics 2500"))
bugs.clientWaitSyncStalls = true;
}
if (getVendor() == VENDOR_INTEL)
{
const char *device = (const char *) glGetString(GL_RENDERER);
if (strstr(device, "HD Graphics 3000") || strstr(device, "HD Graphics 2000")
|| !strcmp(device, "Intel(R) HD Graphics") || !strcmp(device, "Intel(R) HD Graphics Family"))
{
bugs.brokenSRGB = true;
}
}
#endif
#ifdef LOVE_WINDOWS
@@ -210,8 +223,8 @@ void OpenGL::setupContext()
setEnableState(ENABLE_SCISSOR_TEST, state.enableState[ENABLE_SCISSOR_TEST]);
setEnableState(ENABLE_FACE_CULL, state.enableState[ENABLE_FACE_CULL]);
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|| GLAD_EXT_sRGB_write_control)
if (!bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
|| GLAD_EXT_framebuffer_sRGB || GLAD_EXT_sRGB_write_control))
{
setEnableState(ENABLE_FRAMEBUFFER_SRGB, state.enableState[ENABLE_FRAMEBUFFER_SRGB]);
}
@@ -225,9 +238,13 @@ void OpenGL::setupContext()
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
{
state.boundBuffers[i] = 0;
glBindBuffer(getGLBufferType((BufferType) i), 0);
if (isBufferTypeSupported((BufferType) i))
glBindBuffer(getGLBufferType((BufferType) i), 0);
}
if (isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
state.boundIndexedBuffers[BUFFERTYPE_SHADER_STORAGE].resize(maxShaderStorageBufferBindings, 0);
// Initialize multiple texture unit support for shaders.
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
{
@@ -280,10 +297,6 @@ void OpenGL::deInitContext()
}
}
if (state.defaultTexelBuffer != 0)
gl.deleteTexture(state.defaultTexelBuffer);
state.defaultTexelBuffer = 0;
contextInitialized = false;
}
@@ -391,15 +404,23 @@ void OpenGL::initOpenGLFunctions()
}
}
if (GLAD_ES_VERSION_2_0 && GLAD_OES_texture_3D && !GLAD_ES_VERSION_3_0)
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
{
// Function signatures don't match, we'll have to conditionally call it
//fp_glTexImage3D = fp_glTexImage3DOES;
fp_glTexSubImage3D = fp_glTexSubImage3DOES;
fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
// The Nvidia Tegra 3 driver (used by Ouya) claims to support GL_EXT_texture_array but
// segfaults if you actually try to use it. OpenGL ES 2.0 devices should use OES_texture_3D.
// GL_EXT_texture_array is for desktops.
GLAD_EXT_texture_array = false;
if (GLAD_OES_texture_3D)
{
// Function signatures don't match, we'll have to conditionally call it
//fp_glTexImage3D = fp_glTexImage3DOES;
fp_glTexSubImage3D = fp_glTexSubImage3DOES;
fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
}
}
if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex)
@@ -463,11 +484,22 @@ void OpenGL::initMaxValues()
else
maxTextureArrayLayers = 0;
if (areTexelBuffersSupported())
if (isBufferTypeSupported(BUFFERTYPE_TEXEL))
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexelBufferSize);
else
maxTexelBufferSize = 0;
if (isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
{
glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxShaderStorageBufferSize);
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings);
}
else
{
maxShaderStorageBufferSize = 0;
maxShaderStorageBufferBindings = 0;
}
int maxattachments = 1;
int maxdrawbuffers = 1;
@@ -589,6 +621,7 @@ GLenum OpenGL::getGLBufferType(BufferType type)
case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
case BUFFERTYPE_UNIFORM: return GL_UNIFORM_BUFFER;
case BUFFERTYPE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
}
@@ -786,6 +819,12 @@ void OpenGL::deleteBuffer(GLuint buffer)
{
if (state.boundBuffers[i] == buffer)
state.boundBuffers[i] = 0;
for (GLuint &bufferid : state.boundIndexedBuffers[i])
{
if (bufferid == buffer)
bufferid = 0;
}
}
}
@@ -1125,6 +1164,19 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
}
void OpenGL::bindIndexedBuffer(GLuint buffer, BufferType type, int index)
{
auto &bindings = state.boundIndexedBuffers[type];
if (bindings.size() > (size_t) index && buffer != bindings[index])
{
bindings[index] = buffer;
glBindBufferBase(getGLBufferType(type), index, buffer);
// glBindBufferBase affects glBindBuffer as well... for some reason.
state.boundBuffers[type] = buffer;
}
}
void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
@@ -1375,9 +1427,28 @@ bool OpenGL::isTextureTypeSupported(TextureType type) const
return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_array;
case TEXTURE_CUBE:
return GLAD_VERSION_1_3 || GLAD_ES_VERSION_2_0;
default:
case TEXTURE_MAX_ENUM:
return false;
}
return false;
}
bool OpenGL::isBufferTypeSupported(BufferType type) const
{
switch (type)
{
case BUFFERTYPE_VERTEX:
case BUFFERTYPE_INDEX:
return true;
case BUFFERTYPE_TEXEL:
// Not supported in ES until 3.2, which we don't support shaders for...
return GLAD_VERSION_3_1;
case BUFFERTYPE_SHADER_STORAGE:
return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
case BUFFERTYPE_MAX_ENUM:
return false;
}
return false;
}
bool OpenGL::isClampZeroOneTextureWrapSupported() const
@@ -1418,12 +1489,6 @@ bool OpenGL::isMultiFormatMRTSupported() const
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
bool OpenGL::areTexelBuffersSupported() const
{
// Not supported in ES until 3.2, which we don't support shaders for...
return GLAD_VERSION_3_1;
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
@@ -1449,6 +1514,11 @@ int OpenGL::getMaxTexelBufferSize() const
return maxTexelBufferSize;
}
int OpenGL::getMaxShaderStorageBufferSize() const
{
return maxShaderStorageBufferSize;
}
int OpenGL::getMaxRenderTargets() const
{
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
@@ -1464,6 +1534,11 @@ int OpenGL::getMaxTextureUnits() const
return maxTextureUnits;
}
int OpenGL::getMaxShaderStorageBufferBindings() const
{
return maxShaderStorageBufferBindings;
}
float OpenGL::getMaxPointSize() const
{
return maxPointSize;
@@ -1882,6 +1957,8 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
else
return true;
case PIXELFORMAT_RGBA8_UNORM_sRGB:
if (gl.bugs.brokenSRGB)
return false;
if (rendertarget)
{
if (GLAD_VERSION_1_0)
+28 -1
View File
@@ -178,6 +178,13 @@ public:
**/
bool brokenR8PixelFormat;
/**
* Intel HD Graphics drivers on Windows prior to the HD 2500/4000 have
* completely broken sRGB support.
* https://github.com/love2d/love/issues/1592
**/
bool brokenSRGB;
/**
* Other bugs which have workarounds that don't use conditional code at
* the moment:
@@ -312,6 +319,9 @@ public:
GLuint getDefaultTexelBuffer() const { return state.defaultTexelBuffer; }
void setDefaultTexelBuffer(GLuint tex) { state.defaultTexelBuffer = tex; }
GLuint getDefaultStorageBuffer() const { return state.defaultStorageBuffer; }
void setDefaultStorageBuffer(GLuint buf) { state.defaultStorageBuffer = buf; }
/**
* Helper for setting the active texture unit.
*
@@ -331,6 +341,8 @@ public:
void bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit);
void bindIndexedBuffer(GLuint buffer, BufferType type, int index);
/**
* Helper for deleting an OpenGL texture.
* Cleans up if the texture is currently bound.
@@ -350,6 +362,7 @@ public:
bool rawTexStorage(TextureType target, int levels, PixelFormat pixelformat, bool &isSRGB, int width, int height, int depth = 1);
bool isTextureTypeSupported(TextureType type) const;
bool isBufferTypeSupported(BufferType type) const;
bool isClampZeroOneTextureWrapSupported() const;
bool isPixelShaderHighpSupported() const;
bool isInstancingSupported() const;
@@ -357,7 +370,6 @@ public:
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
bool isMultiFormatMRTSupported() const;
bool areTexelBuffersSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -372,6 +384,11 @@ public:
**/
int getMaxTexelBufferSize() const;
/**
* Returns the maximum number of bytes in a shader storage buffer.
**/
int getMaxShaderStorageBufferSize() const;
/**
* Returns the maximum supported number of simultaneous render targets.
**/
@@ -387,6 +404,11 @@ public:
**/
int getMaxTextureUnits() const;
/**
* Returns the maximum number of shader storage buffer bindings.
**/
int getMaxShaderStorageBufferBindings() const;
/**
* Returns the maximum point size.
**/
@@ -451,9 +473,11 @@ private:
int maxCubeTextureSize;
int maxTextureArrayLayers;
int maxTexelBufferSize;
int maxShaderStorageBufferSize;
int maxRenderTargets;
int maxSamples;
int maxTextureUnits;
int maxShaderStorageBufferBindings;
float maxPointSize;
bool coreProfile;
@@ -468,6 +492,8 @@ private:
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM + 1];
std::vector<GLuint> boundIndexedBuffers[BUFFERTYPE_MAX_ENUM];
bool enableState[ENABLE_MAX_ENUM];
GLenum faceCullMode;
@@ -488,6 +514,7 @@ private:
GLuint defaultTexture[TEXTURE_MAX_ENUM];
GLuint defaultTexelBuffer;
GLuint defaultStorageBuffer;
} state;
+167 -25
View File
@@ -38,6 +38,11 @@ namespace graphics
namespace opengl
{
static bool isBuffer(Shader::UniformType utype)
{
return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER;
}
Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
: love::graphics::Shader(vertex, pixel)
, program(0)
@@ -69,7 +74,7 @@ Shader::~Shader()
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
else if (isBuffer(p.second.baseType))
{
for (int i = 0; i < p.second.count; i++)
{
@@ -195,7 +200,7 @@ void Shader::mapActiveUniforms()
u.data = malloc(u.dataSize);
break;
case UNIFORM_MATRIX:
u.dataSize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
u.dataSize = sizeof(float) * ((size_t)u.matrix.rows * u.matrix.columns) * u.count;
u.data = malloc(u.dataSize);
break;
default:
@@ -267,7 +272,7 @@ void Shader::mapActiveUniforms()
break;
case UNIFORM_MATRIX:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.matrix.rows * u.matrix.columns;
offset += (size_t)u.matrix.rows * u.matrix.columns;
break;
default:
break;
@@ -310,13 +315,90 @@ void Shader::mapActiveUniforms()
}
}
if (gl.isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
{
GLint numstoragebuffers = 0;
glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numstoragebuffers);
char namebuffer[2048] = { '\0' };
for (int sindex = 0; sindex < numstoragebuffers; sindex++)
{
UniformInfo u = {};
u.baseType = UNIFORM_STORAGEBUFFER;
GLsizei namelength = 0;
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, sindex, 2048, &namelength, namebuffer);
u.name = std::string(namebuffer, namelength);
u.count = 1;
const auto reflectionit = validationReflection.storageBuffers.find(u.name);
if (reflectionit != validationReflection.storageBuffers.end())
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
}
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
auto oldu = olduniforms.find(u.name);
if (oldu != olduniforms.end())
{
u.data = oldu->second.data;
u.dataSize = oldu->second.dataSize;
u.buffers = oldu->second.buffers;
}
else
{
u.dataSize = sizeof(int) * 1;
u.data = malloc(u.dataSize);
u.ints[0] = -1;
u.buffers = new love::graphics::Buffer * [u.count];
memset(u.buffers, 0, sizeof(Buffer*)* u.count);
}
GLenum props[] = { GL_BUFFER_BINDING };
glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, sindex, 1, props, 1, nullptr, u.ints);
BufferBinding binding;
binding.bindingindex = u.ints[0];
binding.buffer = gl.getDefaultStorageBuffer();
if (binding.bindingindex >= 0)
{
int activeindex = (int)activeStorageBufferBindings.size();
storageBufferBindingIndexToActiveBinding[binding.bindingindex] = activeindex;
activeStorageBufferBindings.push_back(binding);
}
uniforms[u.name] = u;
for (int i = 0; i < u.count; i++)
{
if (u.buffers[i] == nullptr)
continue;
Volatile* v = dynamic_cast<Volatile*>(u.buffers[i]);
if (v != nullptr)
v->loadVolatile();
}
sendBuffers(&u, u.buffers, u.count, true);
}
}
// Make sure uniforms that existed before but don't exist anymore are
// cleaned up. This theoretically shouldn't happen, but...
for (const auto &p : olduniforms)
{
if (uniforms.find(p.first) == uniforms.end())
{
free(p.second.data);
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
{
@@ -328,7 +410,7 @@ void Shader::mapActiveUniforms()
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
else if (isBuffer(p.second.baseType))
{
for (int i = 0; i < p.second.count; i++)
{
@@ -354,6 +436,9 @@ bool Shader::loadVolatile()
textureUnits.clear();
textureUnits.push_back(TextureUnit());
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), -1);
activeStorageBufferBindings.clear();
for (const auto &stage : stages)
{
if (stage.get() != nullptr)
@@ -491,6 +576,9 @@ void Shader::attach()
}
}
for (auto bufferbinding : activeStorageBufferBindings)
gl.bindIndexedBuffer(bufferbinding.buffer, BUFFERTYPE_SHADER_STORAGE, bufferbinding.bindingindex);
// send any pending uniforms to the shader program.
for (const auto &p : pendingUniformUpdates)
updateUniform(p.first, p.second, true);
@@ -713,10 +801,18 @@ static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_TEXELBUFFER)
return;
uint32 requiredtypeflags = 0;
uint32 requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
if (texelbinding)
requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
else if (storagebinding)
requiredtypeflags = Buffer::TYPEFLAG_SHADER_STORAGE;
if (requiredtypeflags == 0)
return;
bool shaderactive = current == this;
@@ -736,17 +832,43 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
{
if (internalUpdate)
continue;
else
else if (texelbinding)
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
else if (storagebinding)
throw love::Exception("Shader uniform '%s' is a shader storage buffer block, but the given Buffer was not created with shader storage buffer capabilities.", info->name.c_str());
else
throw love::Exception("Shader uniform '%s' does not match the types supported by the given Buffer.", info->name.c_str());
}
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
if (texelbinding)
{
if (internalUpdate)
continue;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
{
if (internalUpdate)
continue;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
}
}
else if (storagebinding)
{
if (info->bufferStride != buffer->getArrayStride())
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader storage block '%s' has an array stride of %d bytes, but the given Buffer has an array stride of %d bytes.",
info->name.c_str(), info->bufferStride, buffer->getArrayStride());
}
else if (info->bufferMemberCount != buffer->getDataMembers().size())
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader storage block '%s' has a struct with %d fields, but the given Buffer has a format with %d members.",
info->name.c_str(), info->bufferMemberCount, buffer->getDataMembers().size());
}
}
buffer->retain();
@@ -757,19 +879,39 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
info->buffers[i] = buffer;
GLuint gltex = 0;
if (buffers[i] != nullptr)
gltex = (GLuint) buffer->getTexelBufferHandle();
else
gltex = gl.getDefaultTexelBuffer();
if (texelbinding)
{
GLuint gltex = 0;
if (buffers[i] != nullptr)
gltex = (GLuint) buffer->getTexelBufferHandle();
else
gltex = gl.getDefaultTexelBuffer();
int texunit = info->ints[i];
int texunit = info->ints[i];
if (shaderactive)
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
if (shaderactive)
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
}
else if (storagebinding)
{
int bindingindex = info->ints[i];
GLuint glbuffer = 0;
if (buffers[i] != nullptr)
glbuffer = (GLuint) buffer->getHandle();
else
glbuffer = gl.getDefaultStorageBuffer();
if (shaderactive)
gl.bindIndexedBuffer(glbuffer, BUFFERTYPE_SHADER_STORAGE, bindingindex);
int activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
if (activeindex >= 0)
activeStorageBufferBindings[activeindex].buffer = glbuffer;
}
}
}
+9
View File
@@ -80,6 +80,12 @@ private:
bool active = false;
};
struct BufferBinding
{
int bindingindex = 0;
GLuint buffer = 0;
};
// Map active uniform names to their locations.
void mapActiveUniforms();
@@ -114,6 +120,9 @@ private:
// Texture unit pool for setting textures
std::vector<TextureUnit> textureUnits;
std::vector<int> storageBufferBindingIndexToActiveBinding;
std::vector<BufferBinding> activeStorageBufferBindings;
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
float lastPointSize;
+51 -42
View File
@@ -34,7 +34,7 @@ namespace graphics
namespace opengl
{
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, bool clear)
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int mips, int layers, bool clear)
{
// get currently bound fbo to reset to it later
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
@@ -42,11 +42,6 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
// Intel driver bug: https://github.com/love2d/love/issues/1592
bool current_srgb = gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB);
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, false);
if (texture != 0)
{
if (isPixelFormatDepthStencil(format) && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
@@ -68,37 +63,40 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
// Make sure all faces and layers of the texture are initialized to
// transparent black. This is unfortunately probably pretty slow for
// 2D-array and 3D textures with a lot of layers...
for (int layer = layers - 1; layer >= 0; layer--)
for (int mip = mips - 1; mip >= 0; mip--)
{
for (int face = faces - 1; face >= 0; face--)
for (int layer = layers - 1; layer >= 0; layer--)
{
for (GLenum attachment : fmt.framebufferAttachments)
for (int face = faces - 1; face >= 0; face--)
{
if (attachment == GL_NONE)
continue;
gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
}
if (clear)
{
if (isPixelFormatDepthStencil(format))
for (GLenum attachment : fmt.framebufferAttachments)
{
bool hadDepthWrites = gl.hasDepthWrites();
if (!hadDepthWrites) // glDepthMask also affects glClear.
gl.setDepthWrites(true);
if (attachment == GL_NONE)
continue;
gl.clearDepth(1.0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
gl.framebufferTexture(attachment, texType, texture, mip, layer, face);
}
else
if (clear)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (isPixelFormatDepthStencil(format))
{
bool hadDepthWrites = gl.hasDepthWrites();
if (!hadDepthWrites) // glDepthMask also affects glClear.
gl.setDepthWrites(true);
gl.clearDepth(1.0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
}
else
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
}
}
}
@@ -109,10 +107,6 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
// Restore sRGB state if we turned it off above.
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, current_srgb);
return status;
}
@@ -328,28 +322,43 @@ void Texture::createTexture()
bool hasdata = slices.get(0, 0) != nullptr;
// All mipmap levels need to be initialized - for color formats we can clear
// the base mip and use glGenerateMipmap after that's done. Depth and
// stencil formats don't always support glGenerateMipmap so we need to
// individually clear each mip level in that case. We avoid doing that for
// color formats because of an Intel driver bug:
// https://github.com/love2d/love/issues/1585
int clearmips = 1;
if (isPixelFormatDepthStencil(format))
clearmips = mipmapCount;
// Create a local FBO used for glReadPixels as well as MSAA blitting.
if (isRenderTarget())
{
bool clear = !hasdata;
int slices = texType == TEXTURE_VOLUME ? depth : layers;
framebufferStatus = createFBO(fbo, texType, format, texture, slices, clear);
framebufferStatus = createFBO(fbo, texType, format, texture, clearmips, slices, clear);
}
else if (!hasdata)
{
// Initialize all slices to transparent black.
std::vector<uint8> emptydata(getPixelFormatSliceSize(format, w, h));
for (int mip = 0; mip < clearmips; mip++)
{
int mipw = getPixelWidth(mip);
int miph = getPixelHeight(mip);
std::vector<uint8> emptydata(getPixelFormatSliceSize(format, mipw, miph));
Rect r = {0, 0, w, h};
int slices = texType == TEXTURE_VOLUME ? depth : layers;
slices = texType == TEXTURE_CUBE ? 6 : slices;
for (int i = 0; i < slices; i++)
uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r);
Rect r = {0, 0, mipw, miph};
int slices = texType == TEXTURE_VOLUME ? getDepth(mip) : layers;
slices = texType == TEXTURE_CUBE ? 6 : slices;
for (int i = 0; i < slices; i++)
uploadByteData(format, emptydata.data(), emptydata.size(), mip, i, r);
}
}
// Non-readable textures can't have mipmaps (enforced in the base class),
// so generateMipmaps here is fine - when they aren't already initialized.
if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1)
if (clearmips < mipmapCount && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
generateMipmaps();
}