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 }, { "copybuffertotexture", Graphics::FEATURE_COPY_BUFFER_TO_TEXTURE },
{ "copytexturetobuffer", Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER }, { "copytexturetobuffer", Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER },
{ "copyrendertargettobuffer", Graphics::FEATURE_COPY_RENDER_TARGET_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) 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_BUFFER_TO_TEXTURE,
FEATURE_COPY_TEXTURE_TO_BUFFER, FEATURE_COPY_TEXTURE_TO_BUFFER,
FEATURE_COPY_RENDER_TARGET_TO_BUFFER, FEATURE_COPY_RENDER_TARGET_TO_BUFFER,
FEATURE_MIPMAP_RANGE,
FEATURE_MAX_ENUM FEATURE_MAX_ENUM
}; };
+23 -8
View File
@@ -180,6 +180,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
, samplerState() , samplerState()
, graphicsMemorySize(0) , graphicsMemorySize(0)
{ {
const auto &caps = gfx->getCapabilities();
int requestedMipmapCount = settings.mipmapCount;
if (slices != nullptr && slices->getMipmapCount() > 0 && slices->getSliceCount() > 0) if (slices != nullptr && slices->getMipmapCount() > 0 && slices->getSliceCount() > 0)
{ {
texType = slices->getTextureType(); texType = slices->getTextureType();
@@ -189,8 +192,15 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
int dataMipmaps = 1; int dataMipmaps = 1;
if (slices->validate() && slices->getMipmapCount() > 1) if (slices->validate() && slices->getMipmapCount() > 1)
{
dataMipmaps = slices->getMipmapCount(); dataMipmaps = slices->getMipmapCount();
if (requestedMipmapCount > 0)
requestedMipmapCount = std::min(requestedMipmapCount, dataMipmaps);
else
requestedMipmapCount = dataMipmaps;
}
love::image::ImageDataBase *slice = slices->get(0, 0); love::image::ImageDataBase *slice = slices->get(0, 0);
format = slice->getFormat(); format = slice->getFormat();
@@ -231,7 +241,17 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
mipmapsMode = MIPMAPS_MANUAL; mipmapsMode = MIPMAPS_MANUAL;
if (mipmapsMode != MIPMAPS_NONE) 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; const char *miperr = nullptr;
if (mipmapsMode == MIPMAPS_AUTO && !supportsGenerateMipmaps(miperr)) 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); 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"; const char *textypestr = "unknown";
Texture::getConstant(texType, textypestr); Texture::getConstant(texType, textypestr);
@@ -864,14 +884,8 @@ bool Texture::Slices::validate() const
int w = firstdata->getWidth(); int w = firstdata->getWidth();
int h = firstdata->getHeight(); int h = firstdata->getHeight();
int depth = textureType == TEXTURE_VOLUME ? slicecount : 1;
PixelFormat format = firstdata->getFormat(); 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) if (textureType == TEXTURE_CUBE && w != h)
throw love::Exception("Cube textures must have equal widths and heights for each cube face."); 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 }, { "height", Texture::SETTING_HEIGHT },
{ "layers", Texture::SETTING_LAYERS }, { "layers", Texture::SETTING_LAYERS },
{ "mipmaps", Texture::SETTING_MIPMAPS }, { "mipmaps", Texture::SETTING_MIPMAPS },
{ "mipmapcount", Texture::SETTING_MIPMAP_COUNT },
{ "format", Texture::SETTING_FORMAT }, { "format", Texture::SETTING_FORMAT },
{ "linear", Texture::SETTING_LINEAR }, { "linear", Texture::SETTING_LINEAR },
{ "type", Texture::SETTING_TYPE }, { "type", Texture::SETTING_TYPE },
+2
View File
@@ -166,6 +166,7 @@ public:
SETTING_HEIGHT, SETTING_HEIGHT,
SETTING_LAYERS, SETTING_LAYERS,
SETTING_MIPMAPS, SETTING_MIPMAPS,
SETTING_MIPMAP_COUNT,
SETTING_FORMAT, SETTING_FORMAT,
SETTING_LINEAR, SETTING_LINEAR,
SETTING_TYPE, SETTING_TYPE,
@@ -185,6 +186,7 @@ public:
int layers = 1; // depth for 3D textures int layers = 1; // depth for 3D textures
TextureType type = TEXTURE_2D; TextureType type = TEXTURE_2D;
MipmapsMode mipmaps = MIPMAPS_NONE; MipmapsMode mipmaps = MIPMAPS_NONE;
int mipmapCount = 0; // only used when > 0.
PixelFormat format = PIXELFORMAT_NORMAL; PixelFormat format = PIXELFORMAT_NORMAL;
bool linear = false; bool linear = false;
float dpiScale = 1.0f; 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_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true; capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_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 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
capabilities.limits[LIMIT_POINT_SIZE] = 511; 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_BUFFER_TO_TEXTURE] = gl.isCopyBufferToTextureSupported();
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported(); capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported();
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = gl.isCopyRenderTargetToBufferSupported(); 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_POINT_SIZE] = gl.getMaxPointSize();
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize(); 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); GLenum gltarget = getGLTextureType(target);
TextureFormat fmt = convertPixelFormat(pixelformat, false, isSRGB); 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) if (fmt.swizzled)
{ {
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]); glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_R, fmt.swizzle[0]);
+4
View File
@@ -264,6 +264,10 @@ void Texture::createTexture()
if (!isCompressed()) if (!isCompressed())
gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers); 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 w = pixelWidth;
int h = pixelHeight; int h = pixelHeight;
int d = depth; 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_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true; capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_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; VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physicalDevice, &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_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.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); s.msaa = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_MSAA), s.msaa);