mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Initial support for writing to textures in compute shaders.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user