Initial support for writing to textures in compute shaders.

This commit is contained in:
Alex Szpakowski
2021-08-13 14:43:03 -03:00
parent cffd485976
commit a551fcd492
14 changed files with 383 additions and 50 deletions
+159 -15
View File
@@ -63,7 +63,7 @@ Shader::~Shader()
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
{
for (int i = 0; i < p.second.count; i++)
{
@@ -158,6 +158,26 @@ void Shader::mapActiveUniforms()
for (int i = 0; i < u.count; i++)
textureUnits.push_back(unit);
}
else if (u.baseType == UNIFORM_STORAGETEXTURE)
{
StorageTextureBinding binding = {};
binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
binding.type = u.textureType;
if ((u.access & (ACCESS_READ | ACCESS_WRITE)) != 0)
binding.access = GL_READ_WRITE;
else if ((u.access & ACCESS_WRITE) != 0)
binding.access = GL_WRITE_ONLY;
else if ((u.access & ACCESS_READ) != 0)
binding.access = GL_READ_ONLY;
bool sRGB = false;
auto fmt = OpenGL::convertPixelFormat(u.storageTextureFormat, false, sRGB);
binding.internalFormat = fmt.internalformat;
for (int i = 0; i < u.count; i++)
storageTextureBindings.push_back(binding);
}
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
@@ -183,6 +203,7 @@ void Shader::mapActiveUniforms()
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
case UNIFORM_STORAGETEXTURE:
case UNIFORM_TEXELBUFFER:
u.dataSize = sizeof(int) * u.components * u.count;
u.data = malloc(u.dataSize);
@@ -226,6 +247,17 @@ void Shader::mapActiveUniforms()
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
else if (u.baseType == UNIFORM_STORAGETEXTURE)
{
int startbinding = (int) storageTextureBindings.size() - u.count;
for (int i = 0; i < u.count; i++)
u.ints[i] = startbinding + i;
glUniform1iv(u.location, u.count, u.ints);
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
size_t offset = 0;
@@ -277,7 +309,7 @@ void Shader::mapActiveUniforms()
if (builtin != BUILTIN_MAX_ENUM)
builtinUniformInfo[(int)builtin] = &uniforms[u.name];
if (u.baseType == UNIFORM_SAMPLER)
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
{
// Make sure all stored textures have their Volatiles loaded before
// the sendTextures call, since it calls getHandle().
@@ -401,7 +433,7 @@ void Shader::mapActiveUniforms()
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
{
for (int i = 0; i < p.second.count; i++)
{
@@ -435,6 +467,8 @@ bool Shader::loadVolatile()
textureUnits.clear();
textureUnits.push_back(TextureUnit());
activeStorageBufferBindings.clear();
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), std::make_pair(-1, -1));
activeStorageBufferBindings.clear();
activeWritableStorageBuffers.clear();
@@ -573,7 +607,7 @@ void Shader::attach()
// retain/release happens in Graphics::setShader.
// Make sure all textures are bound to their respective texture units.
for (int i = 0; i < (int) textureUnits.size(); ++i)
for (int i = 0; i < (int) textureUnits.size(); i++)
{
const TextureUnit &unit = textureUnits[i];
if (unit.active)
@@ -585,6 +619,12 @@ void Shader::attach()
}
}
for (size_t i = 0; i < storageTextureBindings.size(); i++)
{
const auto &binding = storageTextureBindings[i];
glBindImageTexture((GLuint) i, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
}
for (auto bufferbinding : activeStorageBufferBindings)
gl.bindIndexedBuffer(bufferbinding.buffer, BUFFERUSAGE_SHADER_STORAGE, bufferbinding.bindingindex);
@@ -648,7 +688,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_STORAGETEXTURE || type == UNIFORM_TEXELBUFFER)
{
switch (info->components)
{
@@ -717,7 +757,10 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_SAMPLER)
bool issampler = info->baseType == UNIFORM_SAMPLER;
bool isstoragetex = info->baseType == UNIFORM_STORAGETEXTURE;
if (!issampler && !isstoragetex)
return;
bool shaderactive = current == this;
@@ -770,6 +813,26 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
else
throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint).");
}
else if (isstoragetex && !tex->isComputeWritable())
{
if (internalUpdate)
continue;
else
throw love::Exception("Texture must be created with the computewrite flag set to true in order to be used with a storage texture (image2D etc) shader uniform variable.");
}
else if (isstoragetex && info->storageTextureFormat != getLinearPixelFormat(tex->getPixelFormat()))
{
if (internalUpdate)
continue;
else
{
const char *texpfstr = "unknown";
const char *shaderpfstr = "unknown";
love::getConstant(getLinearPixelFormat(tex->getPixelFormat()), texpfstr);
love::getConstant(info->storageTextureFormat, shaderpfstr);
throw love::Exception("Texture's pixel format (%s) must match the shader uniform variable %s's pixel format (%s)", texpfstr, info->name.c_str(), shaderpfstr);
}
}
tex->retain();
}
@@ -779,19 +842,39 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
info->textures[i] = tex;
GLuint gltex = 0;
if (textures[i] != nullptr)
gltex = (GLuint) tex->getHandle();
if (isstoragetex)
{
GLuint gltex = 0;
if (tex != nullptr)
gltex = (GLuint) tex->getHandle();
else
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
int bindingindex = info->ints[i];
auto &binding = storageTextureBindings[bindingindex];
binding.texture = tex;
binding.gltexture = gltex;
if (shaderactive)
glBindImageTexture(bindingindex, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
}
else
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
{
GLuint gltex = 0;
if (textures[i] != nullptr)
gltex = (GLuint) tex->getHandle();
else
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
int texunit = info->ints[i];
int texunit = info->ints[i];
if (shaderactive)
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
if (shaderactive)
gl.bindTextureToUnit(info->textureType, 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;
}
}
}
@@ -1243,6 +1326,67 @@ void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u)
u.dataBaseType = DATA_BASETYPE_UINT;
break;
case GL_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D;
break;
case GL_INT_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D;
break;
case GL_UNSIGNED_INT_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D;
break;
case GL_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_INT_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_INT_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_UNSIGNED_INT_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_CUBE;
break;
case GL_INT_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_CUBE;
break;
case GL_UNSIGNED_INT_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_CUBE;
break;
default:
break;
}