Add texture views. Fixes #2022.

Add love.graphics.newTextureView(basetexture, viewsettings). Requires GLSL4 support.
Add 'viewformats' boolean setting to texture setting tables.
Add Texture:hasViewFormats.

The viewsettings table has the following fields:
  format = nil, -- set this to a pixel format to override the base format. It must have the same number of bytes per pixel as the original, and the 'viewformats' setting on the original texture must be true for this to work. Cannot be used for compressed or depth/stencil textures.
  type = nil, -- set this to a texture type to override the base texture type. 2d base textures can make [2d, array] views. [array, cube] base textures can make [2d, array, cube] views.
  mipmapstart = nil, -- set to a number to use a less detailed mipmap level as a base.
  mipmapcount = nil, -- set to a number to override the number of mipmaps in the texture view.
  layerstart = nil, -- set to a number to use a specific layer or cube face as a base.
  layers = nil, -- set to a number to override the number of layers in an array texture view.
This commit is contained in:
Sasha Szpakowski
2024-02-24 15:45:43 -04:00
parent 1e97e09b28
commit 42812ad521
20 changed files with 588 additions and 216 deletions
+5
View File
@@ -158,6 +158,11 @@ love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings,
return new Texture(this, settings, data);
}
love::graphics::Texture *Graphics::newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
{
return new Texture(this, base, viewsettings);
}
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles)
{
return new ShaderStage(this, stage, source, gles, cachekey);
+1
View File
@@ -57,6 +57,7 @@ public:
virtual ~Graphics();
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
love::graphics::Texture *newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings) override;
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
+2
View File
@@ -2506,6 +2506,8 @@ const char *OpenGL::debugSeverityString(GLenum severity)
return "medium";
case GL_DEBUG_SEVERITY_LOW:
return "low";
case GL_DEBUG_SEVERITY_NOTIFICATION:
return "notification";
default:
return "unknown";
}
+43 -14
View File
@@ -244,6 +244,25 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
slices.clear();
}
Texture::Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
: love::graphics::Texture(gfx, base, viewsettings)
, slices(viewsettings.type.get(base->getTextureType()))
, fbo(0)
, texture(0)
, renderbuffer(0)
, framebufferStatus(GL_FRAMEBUFFER_COMPLETE)
, textureGLError(GL_NO_ERROR)
, actualSamples(1)
{
if (!loadVolatile())
{
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE)
throw love::Exception("Cannot create texture view (OpenGL framebuffer error: %s)", OpenGL::framebufferStatusString(framebufferStatus));
if (textureGLError != GL_NO_ERROR)
throw love::Exception("Cannot create texture view (OpenGL error: %s)", OpenGL::errorString(textureGLError));
}
}
Texture::~Texture()
{
unloadVolatile();
@@ -254,11 +273,26 @@ void Texture::createTexture()
// The base class handles some validation. For example, if ImageData is
// given then it must exist for all mip levels, a render target can't use
// a compressed format, etc.
glGenTextures(1, &texture);
GLenum gltype = OpenGL::getGLTextureType(texType);
if (parentView.texture != this)
{
OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false);
Texture *basetex = (Texture *) parentView.texture;
int layers = texType == TEXTURE_CUBE ? 6 : getLayerCount();
glTextureView(texture, gltype, basetex->texture, fmt.internalformat,
parentView.startMipmap, getMipmapCount(),
parentView.startLayer, layers);
gl.bindTextureToUnit(this, 0, false);
setSamplerState(samplerState);
return;
}
gl.bindTextureToUnit(this, 0, false);
GLenum gltype = OpenGL::getGLTextureType(texType);
if (renderTarget && GLAD_ANGLE_texture_usage)
glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
@@ -371,6 +405,12 @@ bool Texture::loadVolatile()
if (texture != 0 || renderbuffer != 0)
return true;
if (parentView.texture != this)
{
Texture *basetex = (Texture *) parentView.texture;
basetex->loadVolatile();
}
OpenGL::TempDebugGroup debuggroup("Texture load");
// NPOT textures don't support mipmapping without full NPOT support.
@@ -593,18 +633,7 @@ void Texture::setSamplerState(const SamplerState &s)
if (s.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported())
throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
// Base class does common validation and assigns samplerState.
love::graphics::Texture::setSamplerState(s);
auto supportedflags = OpenGL::getPixelFormatUsageFlags(getPixelFormat());
if ((supportedflags & PIXELFORMATUSAGEFLAGS_LINEAR) == 0)
{
samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST;
if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST;
}
samplerState = validateSamplerState(s);
// If we only have limited NPOT support then the wrap mode must be CLAMP.
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
+2
View File
@@ -39,6 +39,7 @@ class Texture final : public love::graphics::Texture, public Volatile
public:
Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data);
Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings);
virtual ~Texture();
@@ -61,6 +62,7 @@ public:
void readbackInternal(int slice, int mipmap, const Rect &rect, int destwidth, size_t size, void *dest);
private:
void createTexture();
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;