Allow creating a mipmapped texture with less than the full mip range.

When creating a texture with user-specified mipmaps, it no longer errors if all mipmaps down to 1x1 aren't present.

Added a 'mipmapcount' field to the settings table in newImage and friends (only used when mipmaps are enabled for the texture.) If it's set, only mipmap levels up to the specified count will be created instead of always creating the full range.

Old OpenGL ES 2 devices don't support loading a mipmapped texture without the full mipmap range, so there's a new 'mipmaprange' boolean field in the table returned by love.graphics.getSupported.
This commit is contained in:
Sasha Szpakowski
2023-01-29 16:02:39 -04:00
parent 8706f0ef6a
commit defe811f62
10 changed files with 48 additions and 11 deletions
+1
View File
@@ -2532,6 +2532,7 @@ STRINGMAP_CLASS_BEGIN(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, f
{ "copybuffertotexture", Graphics::FEATURE_COPY_BUFFER_TO_TEXTURE },
{ "copytexturetobuffer", Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER },
{ "copyrendertargettobuffer", Graphics::FEATURE_COPY_RENDER_TARGET_TO_BUFFER },
{ "mipmaprange", Graphics::FEATURE_MIPMAP_RANGE },
}
STRINGMAP_CLASS_END(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, feature)
+1
View File
@@ -164,6 +164,7 @@ public:
FEATURE_COPY_BUFFER_TO_TEXTURE,
FEATURE_COPY_TEXTURE_TO_BUFFER,
FEATURE_COPY_RENDER_TARGET_TO_BUFFER,
FEATURE_MIPMAP_RANGE,
FEATURE_MAX_ENUM
};
+23 -8
View File
@@ -180,6 +180,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
, samplerState()
, graphicsMemorySize(0)
{
const auto &caps = gfx->getCapabilities();
int requestedMipmapCount = settings.mipmapCount;
if (slices != nullptr && slices->getMipmapCount() > 0 && slices->getSliceCount() > 0)
{
texType = slices->getTextureType();
@@ -189,8 +192,15 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
int dataMipmaps = 1;
if (slices->validate() && slices->getMipmapCount() > 1)
{
dataMipmaps = slices->getMipmapCount();
if (requestedMipmapCount > 0)
requestedMipmapCount = std::min(requestedMipmapCount, dataMipmaps);
else
requestedMipmapCount = dataMipmaps;
}
love::image::ImageDataBase *slice = slices->get(0, 0);
format = slice->getFormat();
@@ -231,7 +241,17 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
mipmapsMode = MIPMAPS_MANUAL;
if (mipmapsMode != MIPMAPS_NONE)
mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
{
int totalMipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
if (requestedMipmapCount > 0)
mipmapCount = std::min(totalMipmapCount, requestedMipmapCount);
else
mipmapCount = totalMipmapCount;
if (mipmapCount != totalMipmapCount && !caps.features[Graphics::FEATURE_MIPMAP_RANGE])
throw love::Exception("Custom mipmap ranges for a texture are not supported on this system (%d mipmap levels are required but only %d levels were provided.)", totalMipmapCount, mipmapCount);
}
const char *miperr = nullptr;
if (mipmapsMode == MIPMAPS_AUTO && !supportsGenerateMipmaps(miperr))
@@ -288,7 +308,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
throw love::Exception("The %s%s pixel format is not supported%s on this system.", fstr, readablestr, rtstr);
}
if (!gfx->getCapabilities().textureTypes[texType])
if (!caps.textureTypes[texType])
{
const char *textypestr = "unknown";
Texture::getConstant(texType, textypestr);
@@ -864,14 +884,8 @@ bool Texture::Slices::validate() const
int w = firstdata->getWidth();
int h = firstdata->getHeight();
int depth = textureType == TEXTURE_VOLUME ? slicecount : 1;
PixelFormat format = firstdata->getFormat();
int expectedmips = Texture::getTotalMipmapCount(w, h, depth);
if (mipcount != expectedmips && mipcount != 1)
throw love::Exception("Texture does not have all required mipmap levels (expected %d, got %d)", expectedmips, mipcount);
if (textureType == TEXTURE_CUBE && w != h)
throw love::Exception("Cube textures must have equal widths and heights for each cube face.");
@@ -947,6 +961,7 @@ static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM>::Entry setting
{ "height", Texture::SETTING_HEIGHT },
{ "layers", Texture::SETTING_LAYERS },
{ "mipmaps", Texture::SETTING_MIPMAPS },
{ "mipmapcount", Texture::SETTING_MIPMAP_COUNT },
{ "format", Texture::SETTING_FORMAT },
{ "linear", Texture::SETTING_LINEAR },
{ "type", Texture::SETTING_TYPE },
+2
View File
@@ -166,6 +166,7 @@ public:
SETTING_HEIGHT,
SETTING_LAYERS,
SETTING_MIPMAPS,
SETTING_MIPMAP_COUNT,
SETTING_FORMAT,
SETTING_LINEAR,
SETTING_TYPE,
@@ -185,6 +186,7 @@ public:
int layers = 1; // depth for 3D textures
TextureType type = TEXTURE_2D;
MipmapsMode mipmaps = MIPMAPS_NONE;
int mipmapCount = 0; // only used when > 0.
PixelFormat format = PIXELFORMAT_NORMAL;
bool linear = false;
float dpiScale = 1.0f;
+2 -1
View File
@@ -2172,7 +2172,8 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = true;
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_MIPMAP_RANGE] = true;
static_assert(FEATURE_MAX_ENUM == 18, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
capabilities.limits[LIMIT_POINT_SIZE] = 511;
+2 -1
View File
@@ -1661,7 +1661,8 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = gl.isCopyBufferToTextureSupported();
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported();
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = gl.isCopyRenderTargetToBufferSupported();
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_MIPMAP_RANGE] = GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0;
static_assert(FEATURE_MAX_ENUM == 18, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
+6
View File
@@ -1388,6 +1388,12 @@ bool OpenGL::rawTexStorage(TextureType target, int levels, PixelFormat pixelform
GLenum gltarget = getGLTextureType(target);
TextureFormat fmt = convertPixelFormat(pixelformat, false, isSRGB);
// This shouldn't be needed for glTexStorage, but some drivers don't follow
// the spec apparently.
// https://stackoverflow.com/questions/13859061/does-an-immutable-texture-need-a-gl-texture-max-level
if (GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0)
glTexParameteri(gltarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
if (fmt.swizzled)
{
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]);
+4
View File
@@ -264,6 +264,10 @@ void Texture::createTexture()
if (!isCompressed())
gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
// rawTexStorage handles this for uncompressed textures.
if (isCompressed() && (GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0))
glTexParameteri(gltype, GL_TEXTURE_MAX_LEVEL, mipcount - 1);
int w = pixelWidth;
int h = pixelHeight;
int d = depth;
+2 -1
View File
@@ -566,7 +566,8 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = true;
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_MIPMAP_RANGE] = true;
static_assert(FEATURE_MAX_ENUM == 18, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
+5
View File
@@ -781,6 +781,11 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
}
lua_pop(L, 1);
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_MIPMAP_COUNT));
if (!lua_isnoneornil(L, -1))
s.mipmapCount = (int) luaL_checkinteger(L, -1);
lua_pop(L, 1);
s.linear = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_LINEAR), s.linear);
s.msaa = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_MSAA), s.msaa);