diff --git a/src/modules/graphics/Canvas.cpp b/src/modules/graphics/Canvas.cpp index ac5b65463..df5194ed2 100644 --- a/src/modules/graphics/Canvas.cpp +++ b/src/modules/graphics/Canvas.cpp @@ -72,6 +72,7 @@ Canvas::Canvas(const Settings &settings) } auto gfx = Module::getInstance(Module::M_GRAPHICS); + const Graphics::Capabilities &caps = gfx->getCapabilities(); if (!gfx->isCanvasFormatSupported(format, readable)) { @@ -89,7 +90,7 @@ Canvas::Canvas(const Settings &settings) if (!readable && texType != TEXTURE_2D) throw love::Exception("Non-readable pixel formats are only supported for 2D texture types."); - if (!gfx->isTextureTypeSupported(texType)) + if (!caps.textureTypes[texType]) { const char *textypestr = "unknown"; Texture::getConstant(texType, textypestr); @@ -100,14 +101,14 @@ Canvas::Canvas(const Settings &settings) switch (texType) { case TEXTURE_2D: - maxsize = gfx->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE); + maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE]; if (pixelWidth > maxsize) throw TextureTooLargeException("width", pixelWidth); else if (pixelHeight > maxsize) throw TextureTooLargeException("height", pixelHeight); break; case TEXTURE_VOLUME: - maxsize = gfx->getSystemLimit(Graphics::LIMIT_VOLUME_TEXTURE_SIZE); + maxsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE]; if (pixelWidth > maxsize) throw TextureTooLargeException("width", pixelWidth); else if (pixelHeight > maxsize) @@ -116,18 +117,18 @@ Canvas::Canvas(const Settings &settings) throw TextureTooLargeException("depth", depth); break; case TEXTURE_2D_ARRAY: - maxsize = gfx->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE); + maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE]; if (pixelWidth > maxsize) throw TextureTooLargeException("width", pixelWidth); else if (pixelHeight > maxsize) throw TextureTooLargeException("height", pixelHeight); - else if (layers > gfx->getSystemLimit(Graphics::LIMIT_TEXTURE_LAYERS)) + else if (layers > (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS]) throw TextureTooLargeException("array layer count", layers); break; case TEXTURE_CUBE: if (pixelWidth != pixelHeight) throw love::Exception("Cubemap textures must have equal width and height."); - else if (pixelWidth > gfx->getSystemLimit(Graphics::LIMIT_CUBE_TEXTURE_SIZE)) + else if (pixelWidth > (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE]) throw TextureTooLargeException("width", pixelWidth); break; default: diff --git a/src/modules/graphics/Font.cpp b/src/modules/graphics/Font.cpp index d9f989c28..2530d4a73 100644 --- a/src/modules/graphics/Font.cpp +++ b/src/modules/graphics/Font.cpp @@ -99,7 +99,10 @@ Font::TextureSize Font::getNextTextureSize() const int maxsize = 2048; auto gfx = Module::getInstance(Module::M_GRAPHICS); if (gfx != nullptr) - maxsize = (int) gfx->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE); + { + const auto &caps = gfx->getCapabilities(); + maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE]; + } int maxwidth = std::min(8192, maxsize); int maxheight = std::min(4096, maxsize); diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 571035ff9..2f1430b6d 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -112,6 +112,7 @@ Graphics::Graphics() , streamBufferState() , projectionMatrix() , canvasSwitchCount(0) + , capabilities() { transformStack.reserve(16); transformStack.push_back(Matrix4()); @@ -1142,6 +1143,11 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count) } } +const Graphics::Capabilities &Graphics::getCapabilities() const +{ + return capabilities; +} + Graphics::Stats Graphics::getStats() const { Stats stats; @@ -1451,8 +1457,6 @@ StringMap::Entry Graphics::featur { "lighten", FEATURE_LIGHTEN }, { "fullnpot", FEATURE_FULL_NPOT }, { "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP }, - { "arraytexture", FEATURE_ARRAY_TEXTURE }, - { "volumetexture", FEATURE_VOLUME_TEXTURE }, { "glsl3", FEATURE_GLSL3 }, { "instancing", FEATURE_INSTANCING }, }; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index ed8c68a98..ea5788a6f 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -159,8 +159,6 @@ public: FEATURE_LIGHTEN, FEATURE_FULL_NPOT, FEATURE_PIXEL_SHADER_HIGHP, - FEATURE_ARRAY_TEXTURE, - FEATURE_VOLUME_TEXTURE, FEATURE_GLSL3, FEATURE_INSTANCING, FEATURE_MAX_ENUM @@ -199,6 +197,13 @@ public: TEMPORARY_RT_STENCIL = (1 << 1), }; + struct Capabilities + { + double limits[LIMIT_MAX_ENUM]; + bool features[FEATURE_MAX_ENUM]; + bool textureTypes[TEXTURE_MAX_ENUM]; + }; + struct RendererInfo { std::string name; @@ -696,19 +701,10 @@ public: void polygon(DrawMode mode, const float *coords, size_t count); /** - * Gets whether a graphics feature is supported on this system. + * Gets the graphics capabilities (feature support, limit values, and + * supported texture types) of this system. **/ - virtual bool isSupported(Feature feature) const = 0; - - /** - * Gets whether the given texture type is supported on this system. - **/ - virtual bool isTextureTypeSupported(TextureType textype) const = 0; - - /** - * Gets the system-dependent numeric limit for the specified parameter. - **/ - virtual double getSystemLimit(SystemLimit limittype) const = 0; + const Capabilities &getCapabilities() const; /** * Gets whether the specified pixel format is supported by Canvases or @@ -864,6 +860,7 @@ protected: virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0; + virtual void initCapabilities() = 0; virtual void getAPIStats(int &drawcalls, int &shaderswitches) const = 0; Canvas *getTemporaryCanvas(PixelFormat format, int w, int h, int samples); @@ -903,6 +900,8 @@ protected: int canvasSwitchCount; + Capabilities capabilities; + static const size_t MAX_USER_STACK_DEPTH = 64; private: diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index f1a1a41a8..45f8a5c24 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -303,7 +303,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step) { auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (step == STEP_PER_INSTANCE && !gfx->isSupported(Graphics::FEATURE_INSTANCING)) + if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) throw love::Exception("Vertex attribute instancing is not supported on this system."); if (mesh != this) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 1fe2ae3a8..0e220d829 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -233,7 +233,7 @@ bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool return false; } - bool supportsGLSL3 = gfx->isSupported(Graphics::FEATURE_GLSL3); + bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3]; int defaultversion = gles ? 100 : 120; EProfile defaultprofile = ENoProfile; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index bac024424..03145126e 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -202,6 +202,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b gl.setupContext(); created = true; + initCapabilities(); setViewportSize(width, height, pixelwidth, pixelheight); @@ -272,7 +273,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b // We always need a default shader. for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { - if (i == Shader::STANDARD_ARRAY && !isSupported(FEATURE_ARRAY_TEXTURE)) + if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY]) continue; if (!Shader::standardShaders[i]) @@ -1282,7 +1283,7 @@ void Graphics::setBlendMode(BlendMode mode, BlendAlpha alphamode) if (mode == BLEND_LIGHTEN || mode == BLEND_DARKEN) { - if (!isSupported(FEATURE_LIGHTEN)) + if (!capabilities.features[FEATURE_LIGHTEN]) throw love::Exception("The 'lighten' and 'darken' blend modes are not supported on this system."); } @@ -1410,61 +1411,29 @@ void Graphics::getAPIStats(int &drawcalls, int &shaderswitches) const shaderswitches = gl.stats.shaderSwitches; } -double Graphics::getSystemLimit(SystemLimit limittype) const +void Graphics::initCapabilities() { - switch (limittype) - { - case LIMIT_POINT_SIZE: - return (double) gl.getMaxPointSize(); - case LIMIT_TEXTURE_SIZE: - return (double) gl.getMax2DTextureSize(); - case LIMIT_TEXTURE_LAYERS: - return (double) gl.getMaxTextureLayers(); - case LIMIT_VOLUME_TEXTURE_SIZE: - return (double) gl.getMax3DTextureSize(); - case LIMIT_CUBE_TEXTURE_SIZE: - return (double) gl.getMaxCubeTextureSize(); - case LIMIT_MULTI_CANVAS: - return (double) gl.getMaxRenderTargets(); - case LIMIT_CANVAS_MSAA: - return (double) gl.getMaxRenderbufferSamples(); - case LIMIT_ANISOTROPY: - return (double) gl.getMaxAnisotropy(); - default: - return 0.0; - } -} + capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = Canvas::isMultiFormatMultiCanvasSupported(); + capabilities.features[FEATURE_CLAMP_ZERO] = gl.isClampZeroTextureWrapSupported(); + capabilities.features[FEATURE_LIGHTEN] = GLAD_VERSION_1_4 || GLAD_ES_VERSION_3_0 || GLAD_EXT_blend_minmax; + capabilities.features[FEATURE_FULL_NPOT] = GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot; + capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = gl.isPixelShaderHighpSupported(); + capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile(); + capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported(); + static_assert(FEATURE_MAX_ENUM == 7, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); -bool Graphics::isSupported(Feature feature) const -{ - switch (feature) - { - case FEATURE_MULTI_CANVAS_FORMATS: - return Canvas::isMultiFormatMultiCanvasSupported(); - case FEATURE_CLAMP_ZERO: - return gl.isClampZeroTextureWrapSupported(); - case FEATURE_LIGHTEN: - return GLAD_VERSION_1_4 || GLAD_ES_VERSION_3_0 || GLAD_EXT_blend_minmax; - case FEATURE_FULL_NPOT: - return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot; - case FEATURE_PIXEL_SHADER_HIGHP: - return gl.isPixelShaderHighpSupported(); - case FEATURE_ARRAY_TEXTURE: - return gl.isTextureTypeSupported(TEXTURE_2D_ARRAY); - case FEATURE_VOLUME_TEXTURE: - return gl.isTextureTypeSupported(TEXTURE_VOLUME); - case FEATURE_GLSL3: - return GLAD_ES_VERSION_3_0 || gl.isCoreProfile(); - case FEATURE_INSTANCING: - return gl.isInstancingSupported(); - default: - return false; - } -} + capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize(); + capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize(); + capabilities.limits[LIMIT_TEXTURE_LAYERS] = gl.getMaxTextureLayers(); + capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize(); + capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize(); + capabilities.limits[LIMIT_MULTI_CANVAS] = gl.getMaxRenderTargets(); + capabilities.limits[LIMIT_CANVAS_MSAA] = gl.getMaxRenderbufferSamples(); + capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy(); + static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!"); -bool Graphics::isTextureTypeSupported(TextureType textype) const -{ - return gl.isTextureTypeSupported(textype); + for (int i = 0; i < TEXTURE_MAX_ENUM; i++) + capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i); } bool Graphics::isCanvasFormatSupported(PixelFormat format) const diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 2ded0566a..0c7bfc17e 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -117,9 +117,6 @@ public: void setWireframe(bool enable) override; - bool isSupported(Feature feature) const override; - bool isTextureTypeSupported(TextureType textype) const override; - double getSystemLimit(SystemLimit limittype) const override; bool isCanvasFormatSupported(PixelFormat format) const override; bool isCanvasFormatSupported(PixelFormat format, bool readable) const override; bool isImageFormatSupported(PixelFormat format) const override; @@ -131,6 +128,7 @@ public: private: love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override; + void initCapabilities() override; void getAPIStats(int &drawcalls, int &shaderswitches) const override; void endPass(); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index b2a896dc7..c6b4ce142 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1926,6 +1926,7 @@ int w_setDefaultShaderCode(lua_State *L) int w_getSupported(lua_State *L) { + const Graphics::Capabilities &caps = instance()->getCapabilities(); lua_createtable(L, 0, (int) Graphics::FEATURE_MAX_ENUM); for (int i = 0; i < (int) Graphics::FEATURE_MAX_ENUM; i++) @@ -1936,7 +1937,7 @@ int w_getSupported(lua_State *L) if (!Graphics::getConstant(feature, name)) continue; - luax_pushboolean(L, instance()->isSupported(feature)); + luax_pushboolean(L, caps.features[i]); lua_setfield(L, -2, name); } @@ -2011,6 +2012,26 @@ int w_getImageFormats(lua_State *L) return w__getFormats(L, supported, ignore); } +int w_getTextureTypes(lua_State *L) +{ + const Graphics::Capabilities &caps = instance()->getCapabilities(); + lua_createtable(L, 0, (int) TEXTURE_MAX_ENUM); + + for (int i = 0; i < (int) TEXTURE_MAX_ENUM; i++) + { + TextureType textype = (TextureType) i; + const char *name = nullptr; + + if (!Texture::getConstant(textype, name)) + continue; + + lua_pushnumber(L, caps.textureTypes[i]); + lua_setfield(L, -2, name); + } + + return 1; +} + int w_getRendererInfo(lua_State *L) { Graphics::RendererInfo info; @@ -2025,6 +2046,7 @@ int w_getRendererInfo(lua_State *L) int w_getSystemLimits(lua_State *L) { + const Graphics::Capabilities &caps = instance()->getCapabilities(); lua_createtable(L, 0, (int) Graphics::LIMIT_MAX_ENUM); for (int i = 0; i < (int) Graphics::LIMIT_MAX_ENUM; i++) @@ -2035,7 +2057,7 @@ int w_getSystemLimits(lua_State *L) if (!Graphics::getConstant(limittype, name)) continue; - lua_pushnumber(L, instance()->getSystemLimit(limittype)); + lua_pushnumber(L, caps.limits[i]); lua_setfield(L, -2, name); } @@ -2685,6 +2707,7 @@ static const luaL_Reg functions[] = { "getImageFormats", w_getImageFormats }, { "getRendererInfo", w_getRendererInfo }, { "getSystemLimits", w_getSystemLimits }, + { "getTextureTypes", w_getTextureTypes }, { "getStats", w_getStats }, { "captureScreenshot", w_captureScreenshot },