mirror of
https://github.com/love2d/love.git
synced 2026-08-15 15:51:12 +02:00
Add mipmapping support to Canvases.
- Added an optional mipmap index argument to the non-table variant of love.graphics.setCanvas, and an optional ‘mipmap’ field to the table variant. - Canvas:setMipmapFilter now works. - Added Canvas:generateMipmaps. - Added Canvas:getMipmapMode. - Added a new ‘mipmaps’ enum field to the table passed into love.graphics.newCanvas. Accepted values are “none” (default), “manual”, and “auto”. If a Canvas has the manual mipmap mode, you will either need to render to a mipmap level or call Canvas:generateMipmaps. If it has the auto mode, mipmaps will be automatically generated after rendering to that Canvas. Generating mipmaps is not free. --HG-- branch : minor
This commit is contained in:
@@ -29,9 +29,44 @@ namespace graphics
|
||||
love::Type Canvas::type("Canvas", &Texture::type);
|
||||
int Canvas::canvasCount = 0;
|
||||
|
||||
Canvas::Canvas(TextureType textype)
|
||||
: Texture(textype)
|
||||
Canvas::Canvas(const Settings &settings)
|
||||
: Texture(settings.type)
|
||||
{
|
||||
this->settings = settings;
|
||||
|
||||
width = settings.width;
|
||||
height = settings.height;
|
||||
pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
|
||||
pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
|
||||
|
||||
format = settings.format;
|
||||
|
||||
if (texType == TEXTURE_VOLUME)
|
||||
depth = settings.layers;
|
||||
else if (texType == TEXTURE_2D_ARRAY)
|
||||
layers = settings.layers;
|
||||
else
|
||||
layers = 1;
|
||||
|
||||
if (width <= 0 || height <= 0 || layers <= 0)
|
||||
throw love::Exception("Canvas dimensions must be greater than 0.");
|
||||
|
||||
if (texType != TEXTURE_2D && settings.msaa > 1)
|
||||
throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
|
||||
|
||||
if (settings.readable.hasValue)
|
||||
readable = settings.readable.value;
|
||||
else
|
||||
readable = !isPixelFormatDepthStencil(format);
|
||||
|
||||
if (readable && isPixelFormatDepthStencil(format) && settings.msaa > 1)
|
||||
throw love::Exception("Readable depth/stencil Canvases with MSAA are not currently supported.");
|
||||
|
||||
if ((!readable || settings.msaa > 1) && settings.mipmaps != MIPMAP_NONE)
|
||||
throw love::Exception("Non-readable and MSAA textures cannot have mipmaps.");
|
||||
|
||||
mipmapCount = settings.mipmaps == MIPMAP_NONE ? 1 : getMipmapCount(pixelWidth, pixelHeight);
|
||||
|
||||
canvasCount++;
|
||||
}
|
||||
|
||||
@@ -40,6 +75,16 @@ Canvas::~Canvas()
|
||||
canvasCount--;
|
||||
}
|
||||
|
||||
Canvas::MipmapMode Canvas::getMipmapMode() const
|
||||
{
|
||||
return settings.mipmaps;
|
||||
}
|
||||
|
||||
int Canvas::getRequestedMSAA() const
|
||||
{
|
||||
return settings.msaa;
|
||||
}
|
||||
|
||||
void Canvas::draw(Graphics *gfx, Quad *q, const Matrix4 &t)
|
||||
{
|
||||
if (gfx->isCanvasActive(this))
|
||||
@@ -56,6 +101,25 @@ void Canvas::drawLayer(Graphics *gfx, int layer, Quad *quad, const Matrix4 &m)
|
||||
Texture::drawLayer(gfx, layer, quad, m);
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(const char *in, MipmapMode &out)
|
||||
{
|
||||
return mipmapModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(MipmapMode in, const char *&out)
|
||||
{
|
||||
return mipmapModes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Canvas::MipmapMode, Canvas::MIPMAP_MAX_ENUM>::Entry Canvas::mipmapEntries[] =
|
||||
{
|
||||
{ "none", MIPMAP_NONE },
|
||||
{ "manual", MIPMAP_MANUAL },
|
||||
{ "auto", MIPMAP_AUTO },
|
||||
};
|
||||
|
||||
StringMap<Canvas::MipmapMode, Canvas::MIPMAP_MAX_ENUM> Canvas::mipmapModes(Canvas::mipmapEntries, sizeof(Canvas::mipmapEntries));
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "image/Image.h"
|
||||
#include "image/ImageData.h"
|
||||
#include "Texture.h"
|
||||
#include "common/Optional.h"
|
||||
#include "common/StringMap.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -37,38 +39,55 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
enum MipmapMode
|
||||
{
|
||||
MIPMAP_NONE,
|
||||
MIPMAP_MANUAL,
|
||||
MIPMAP_AUTO,
|
||||
MIPMAP_MAX_ENUM
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
// Defaults to true for color pixel formats, and false for depth/stencil.
|
||||
struct Readable
|
||||
{
|
||||
bool set = false;
|
||||
bool value = false;
|
||||
};
|
||||
|
||||
int width = 1;
|
||||
int height = 1;
|
||||
int layers = 1; // depth for 3D textures
|
||||
MipmapMode mipmaps = MIPMAP_NONE;
|
||||
PixelFormat format = PIXELFORMAT_NORMAL;
|
||||
TextureType type = TEXTURE_2D;
|
||||
float pixeldensity = 1.0f;
|
||||
int msaa = 0;
|
||||
Readable readable;
|
||||
OptionalBool readable;
|
||||
};
|
||||
|
||||
Canvas(TextureType textype);
|
||||
Canvas(const Settings &settings);
|
||||
virtual ~Canvas();
|
||||
|
||||
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int x, int y, int w, int h) = 0;
|
||||
MipmapMode getMipmapMode() const;
|
||||
int getRequestedMSAA() const;
|
||||
|
||||
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) = 0;
|
||||
virtual void generateMipmaps() = 0;
|
||||
|
||||
virtual int getMSAA() const = 0;
|
||||
virtual int getRequestedMSAA() const = 0;
|
||||
virtual ptrdiff_t getRenderTargetHandle() const = 0;
|
||||
|
||||
void draw(Graphics *gfx, Quad *q, const Matrix4 &t) override;
|
||||
void drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &t) override;
|
||||
|
||||
static int canvasCount;
|
||||
|
||||
static bool getConstant(const char *in, MipmapMode &out);
|
||||
static bool getConstant(MipmapMode in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
Settings settings;
|
||||
|
||||
private:
|
||||
|
||||
static StringMap<MipmapMode, MIPMAP_MAX_ENUM>::Entry mipmapEntries[];
|
||||
static StringMap<MipmapMode, MIPMAP_MAX_ENUM> mipmapModes;
|
||||
|
||||
}; // Canvas
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
||||
{
|
||||
const auto &rt1 = sRTs.colors[i];
|
||||
const auto &rt2 = curRTs.colors[i];
|
||||
if (rt1.canvas.get() != rt2.canvas.get() || rt1.slice != rt2.slice)
|
||||
if (rt1.canvas.get() != rt2.canvas.get() || rt1.slice != rt2.slice || rt1.mipmap != rt2.mipmap)
|
||||
{
|
||||
canvaseschanged = true;
|
||||
break;
|
||||
@@ -309,7 +309,8 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
||||
}
|
||||
|
||||
if (!canvaseschanged && (sRTs.depthStencil.canvas.get() != curRTs.depthStencil.canvas.get()
|
||||
|| sRTs.depthStencil.slice != curRTs.depthStencil.slice))
|
||||
|| sRTs.depthStencil.slice != curRTs.depthStencil.slice
|
||||
|| sRTs.depthStencil.mipmap != curRTs.depthStencil.mipmap))
|
||||
{
|
||||
canvaseschanged = true;
|
||||
}
|
||||
@@ -422,9 +423,9 @@ void Graphics::setCanvas(const RenderTargetsStrongRef &rts)
|
||||
targets.colors.reserve(rts.colors.size());
|
||||
|
||||
for (const auto &rt : rts.colors)
|
||||
targets.colors.emplace_back(rt.canvas.get(), rt.slice);
|
||||
targets.colors.emplace_back(rt.canvas.get(), rt.slice, rt.mipmap);
|
||||
|
||||
targets.depthStencil = RenderTarget(rts.depthStencil.canvas, rts.depthStencil.slice);
|
||||
targets.depthStencil = RenderTarget(rts.depthStencil.canvas, rts.depthStencil.slice, rts.depthStencil.mipmap);
|
||||
|
||||
return setCanvas(targets);
|
||||
}
|
||||
@@ -437,9 +438,9 @@ Graphics::RenderTargets Graphics::getCanvas() const
|
||||
rts.colors.reserve(curRTs.colors.size());
|
||||
|
||||
for (const auto &rt : curRTs.colors)
|
||||
rts.colors.emplace_back(rt.canvas.get(), rt.slice);
|
||||
rts.colors.emplace_back(rt.canvas.get(), rt.slice, rt.mipmap);
|
||||
|
||||
rts.depthStencil = RenderTarget(curRTs.depthStencil.canvas, curRTs.depthStencil.slice);
|
||||
rts.depthStencil = RenderTarget(curRTs.depthStencil.canvas, curRTs.depthStencil.slice, curRTs.depthStencil.mipmap);
|
||||
|
||||
return rts;
|
||||
}
|
||||
|
||||
@@ -292,10 +292,12 @@ public:
|
||||
{
|
||||
Canvas *canvas;
|
||||
int slice;
|
||||
int mipmap;
|
||||
|
||||
RenderTarget(Canvas *canvas, int slice = 0)
|
||||
RenderTarget(Canvas *canvas, int slice = 0, int mipmap = 0)
|
||||
: canvas(canvas)
|
||||
, slice(slice)
|
||||
, mipmap(mipmap)
|
||||
{}
|
||||
|
||||
RenderTarget()
|
||||
@@ -308,10 +310,12 @@ public:
|
||||
{
|
||||
StrongRef<Canvas> canvas;
|
||||
int slice = 0;
|
||||
int mipmap = 0;
|
||||
|
||||
RenderTargetStrongRef(Canvas *canvas, int slice = 0)
|
||||
RenderTargetStrongRef(Canvas *canvas, int slice = 0, int mipmap = 0)
|
||||
: canvas(canvas)
|
||||
, slice(slice)
|
||||
, mipmap(mipmap)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -175,19 +175,19 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
|
||||
}
|
||||
}
|
||||
|
||||
int Texture::getWidth() const
|
||||
int Texture::getWidth(int mip) const
|
||||
{
|
||||
return width;
|
||||
return std::max(width >> mip, 1);
|
||||
}
|
||||
|
||||
int Texture::getHeight() const
|
||||
int Texture::getHeight(int mip) const
|
||||
{
|
||||
return height;
|
||||
return std::max(height >> mip, 1);
|
||||
}
|
||||
|
||||
int Texture::getDepth() const
|
||||
int Texture::getDepth(int mip) const
|
||||
{
|
||||
return depth;
|
||||
return std::max(depth >> mip, 1);
|
||||
}
|
||||
|
||||
int Texture::getLayerCount() const
|
||||
@@ -200,14 +200,14 @@ int Texture::getMipmapCount() const
|
||||
return mipmapCount;
|
||||
}
|
||||
|
||||
int Texture::getPixelWidth() const
|
||||
int Texture::getPixelWidth(int mip) const
|
||||
{
|
||||
return pixelWidth;
|
||||
return std::max(pixelWidth >> mip, 1);
|
||||
}
|
||||
|
||||
int Texture::getPixelHeight() const
|
||||
int Texture::getPixelHeight(int mip) const
|
||||
{
|
||||
return pixelHeight;
|
||||
return std::max(pixelHeight >> mip, 1);
|
||||
}
|
||||
|
||||
float Texture::getPixelDensity() const
|
||||
@@ -215,6 +215,21 @@ float Texture::getPixelDensity() const
|
||||
return (float) pixelHeight / (float) height;
|
||||
}
|
||||
|
||||
void Texture::setFilter(const Filter &f)
|
||||
{
|
||||
if (!validateFilter(f, getMipmapCount() > 1))
|
||||
{
|
||||
if (f.mipmap != FILTER_NONE && getMipmapCount() == 1)
|
||||
throw love::Exception("Non-mipmapped texture cannot have mipmap filtering.");
|
||||
else
|
||||
throw love::Exception("Invalid texture filter.");
|
||||
}
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
|
||||
filter = f;
|
||||
}
|
||||
|
||||
const Texture::Filter &Texture::getFilter() const
|
||||
{
|
||||
return filter;
|
||||
|
||||
@@ -124,18 +124,18 @@ public:
|
||||
|
||||
bool isReadable() const;
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getDepth() const;
|
||||
int getWidth(int mip = 0) const;
|
||||
int getHeight(int mip = 0) const;
|
||||
int getDepth(int mip = 0) const;
|
||||
int getLayerCount() const;
|
||||
int getMipmapCount() const;
|
||||
|
||||
virtual int getPixelWidth() const;
|
||||
virtual int getPixelHeight() const;
|
||||
int getPixelWidth(int mip = 0) const;
|
||||
int getPixelHeight(int mip = 0) const;
|
||||
|
||||
float getPixelDensity() const;
|
||||
|
||||
virtual void setFilter(const Filter &f) = 0;
|
||||
virtual void setFilter(const Filter &f);
|
||||
virtual const Filter &getFilter() const;
|
||||
|
||||
virtual bool setWrap(const Wrap &w) = 0;
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers)
|
||||
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, int mips)
|
||||
{
|
||||
// get currently bound fbo to reset to it later
|
||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
@@ -48,16 +48,23 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
// Make sure all faces and layers of the texture are initialized to
|
||||
// transparent black. This is unfortunately probably pretty slow for
|
||||
// 2D-array and 3D textures with a lot of layers...
|
||||
for (int layer = layers - 1; layer >= 0; layer--)
|
||||
for (int mip = mips - 1; mip >= 0; mip--)
|
||||
{
|
||||
for (int face = faces - 1; face >= 0; face--)
|
||||
{
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
{
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
int nlayers = layers;
|
||||
if (texType == TEXTURE_VOLUME)
|
||||
nlayers = std::max(layers >> mip, 1);
|
||||
|
||||
gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
|
||||
for (int layer = nlayers - 1; layer >= 0; layer--)
|
||||
{
|
||||
for (int face = faces - 1; face >= 0; face--)
|
||||
{
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
{
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
|
||||
gl.framebufferTexture(attachment, texType, texture, mip, layer, face);
|
||||
}
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
{
|
||||
@@ -146,41 +153,14 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Settings &settings)
|
||||
: love::graphics::Canvas(settings.type)
|
||||
: love::graphics::Canvas(settings)
|
||||
, fbo(0)
|
||||
, texture(0)
|
||||
, renderbuffer(0)
|
||||
, requestedSamples(settings.msaa)
|
||||
, actualSamples(0)
|
||||
, textureMemory(0)
|
||||
{
|
||||
width = settings.width;
|
||||
height = settings.height;
|
||||
pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
|
||||
pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
|
||||
|
||||
if (texType == TEXTURE_VOLUME)
|
||||
depth = settings.layers;
|
||||
else if (texType == TEXTURE_2D_ARRAY)
|
||||
layers = settings.layers;
|
||||
else
|
||||
layers = 1;
|
||||
|
||||
if (width <= 0 || height <= 0 || layers <= 0)
|
||||
throw love::Exception("Canvas dimensions must be greater than 0.");
|
||||
|
||||
if (texType != TEXTURE_2D && settings.msaa > 1)
|
||||
throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
|
||||
|
||||
format = getSizedFormat(settings.format);
|
||||
|
||||
if (settings.readable.set)
|
||||
readable = settings.readable.value;
|
||||
else
|
||||
readable = !isPixelFormatDepthStencil(format);
|
||||
|
||||
if (readable && isPixelFormatDepthStencil(format) && settings.msaa > 1)
|
||||
throw love::Exception("Readable depth/stencil Canvases with MSAA are not currently supported.");
|
||||
format = getSizedFormat(format);
|
||||
|
||||
initQuad();
|
||||
loadVolatile();
|
||||
@@ -212,7 +192,7 @@ bool Canvas::loadVolatile()
|
||||
throw love::Exception("The %s%s canvas format is not supported by your OpenGL drivers.", fstr, readablestr);
|
||||
}
|
||||
|
||||
if (requestedSamples > 1 && texType != TEXTURE_2D)
|
||||
if (getRequestedMSAA() > 1 && texType != TEXTURE_2D)
|
||||
throw love::Exception("MSAA is only supported for 2D texture types.");
|
||||
|
||||
if (!readable && texType != TEXTURE_2D)
|
||||
@@ -221,7 +201,7 @@ bool Canvas::loadVolatile()
|
||||
if (!gl.isTextureTypeSupported(texType))
|
||||
{
|
||||
const char *textypestr = "unknown";
|
||||
getConstant(texType, textypestr);
|
||||
Texture::getConstant(texType, textypestr);
|
||||
throw love::Exception("%s textures are not supported on this system!", textypestr);
|
||||
}
|
||||
|
||||
@@ -277,13 +257,14 @@ bool Canvas::loadVolatile()
|
||||
|
||||
setFilter(filter);
|
||||
setWrap(wrap);
|
||||
setMipmapSharpness(mipmapSharpness);
|
||||
setDepthSampleMode(depthCompareMode);
|
||||
|
||||
while (glGetError() != GL_NO_ERROR)
|
||||
/* Clear the error buffer. */;
|
||||
|
||||
bool isSRGB = format == PIXELFORMAT_sRGBA8;
|
||||
if (!gl.rawTexStorage(texType, 1, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
|
||||
if (!gl.rawTexStorage(texType, mipmapCount, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
|
||||
{
|
||||
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
||||
return false;
|
||||
@@ -298,7 +279,7 @@ bool Canvas::loadVolatile()
|
||||
}
|
||||
|
||||
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
|
||||
status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers);
|
||||
status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers, mipmapCount);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
@@ -313,7 +294,7 @@ bool Canvas::loadVolatile()
|
||||
|
||||
// getMaxRenderbufferSamples will be 0 on systems that don't support
|
||||
// multisampled renderbuffers / don't export FBO multisample extensions.
|
||||
actualSamples = requestedSamples;
|
||||
actualSamples = getRequestedMSAA();
|
||||
actualSamples = std::min(actualSamples, gl.getMaxRenderbufferSamples());
|
||||
actualSamples = std::max(actualSamples, 0);
|
||||
actualSamples = actualSamples == 1 ? 0 : actualSamples;
|
||||
@@ -330,6 +311,9 @@ bool Canvas::loadVolatile()
|
||||
else if (actualSamples > 0)
|
||||
textureMemory *= actualSamples;
|
||||
|
||||
if (getMipmapCount() > 1)
|
||||
textureMemory *= 1.33334;
|
||||
|
||||
gl.updateTextureMemorySize(prevmemsize, textureMemory);
|
||||
|
||||
return true;
|
||||
@@ -356,12 +340,16 @@ void Canvas::unloadVolatile()
|
||||
|
||||
void Canvas::setFilter(const Texture::Filter &f)
|
||||
{
|
||||
if (!validateFilter(f, false))
|
||||
throw love::Exception("Invalid texture filter.");
|
||||
Texture::setFilter(f);
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
|
||||
{
|
||||
filter.mag = filter.min = FILTER_NEAREST;
|
||||
|
||||
if (filter.mipmap == FILTER_LINEAR)
|
||||
filter.mipmap = FILTER_NEAREST;
|
||||
}
|
||||
|
||||
filter = f;
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
gl.setTextureFilter(texType, filter);
|
||||
}
|
||||
@@ -402,9 +390,25 @@ bool Canvas::setWrap(const Texture::Wrap &w)
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Canvas::setMipmapSharpness(float /*sharpness*/)
|
||||
bool Canvas::setMipmapSharpness(float sharpness)
|
||||
{
|
||||
return false;
|
||||
if (!gl.isSamplerLODBiasSupported())
|
||||
return false;
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
|
||||
float maxbias = gl.getMaxLODBias();
|
||||
if (maxbias > 0.01f)
|
||||
maxbias -= 0.0f;
|
||||
|
||||
mipmapSharpness = std::min(std::max(sharpness, -maxbias), maxbias);
|
||||
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
// negative bias is sharper
|
||||
glTexParameterf(gl.getGLTextureType(texType), GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Canvas::setDepthSampleMode(Optional<CompareMode> mode)
|
||||
@@ -413,11 +417,11 @@ void Canvas::setDepthSampleMode(Optional<CompareMode> mode)
|
||||
|
||||
bool supported = gl.isDepthCompareSampleSupported();
|
||||
|
||||
if (mode.hasValue && !supported)
|
||||
throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
|
||||
|
||||
if (mode.hasValue)
|
||||
{
|
||||
if (!supported)
|
||||
throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
|
||||
gl.bindTextureToUnit(texType, texture, 0, false);
|
||||
@@ -448,15 +452,18 @@ ptrdiff_t Canvas::getHandle() const
|
||||
return texture;
|
||||
}
|
||||
|
||||
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int x, int y, int w, int h)
|
||||
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
|
||||
{
|
||||
if (!isReadable())
|
||||
throw love::Exception("Canvas:newImageData cannot be called on non-readable Canvases.");
|
||||
|
||||
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getPixelWidth() || (y + h) > getPixelHeight())
|
||||
if (isPixelFormatDepthStencil(getPixelFormat()))
|
||||
throw love::Exception("Canvas:newImageData cannot be called on Canvases with depth/stencil pixel formats.");
|
||||
|
||||
if (r.x < 0 || r.y < 0 || r.w <= 0 || r.h <= 0 || (r.x + r.w) > getPixelWidth(mipmap) || (r.y + r.h) > getPixelHeight(mipmap))
|
||||
throw love::Exception("Invalid rectangle dimensions.");
|
||||
|
||||
if (slice < 0 || (texType == TEXTURE_VOLUME && slice >= depth)
|
||||
if (slice < 0 || (texType == TEXTURE_VOLUME && slice >= getDepth(mipmap))
|
||||
|| (texType == TEXTURE_2D_ARRAY && slice >= layers)
|
||||
|| (texType == TEXTURE_CUBE && slice >= 6))
|
||||
{
|
||||
@@ -489,7 +496,7 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
||||
break;
|
||||
}
|
||||
|
||||
love::image::ImageData *imagedata = module->newImageData(w, h, dataformat);
|
||||
love::image::ImageData *imagedata = module->newImageData(r.w, r.h, dataformat);
|
||||
|
||||
bool isSRGB = false;
|
||||
OpenGL::TextureFormat fmt = gl.convertPixelFormat(dataformat, false, isSRGB);
|
||||
@@ -497,16 +504,16 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
|
||||
|
||||
if (slice > 0)
|
||||
if (slice > 0 || mipmap > 0)
|
||||
{
|
||||
int layer = texType == TEXTURE_CUBE ? 0 : slice;
|
||||
int face = texType == TEXTURE_CUBE ? slice : 0;
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, layer, face);
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, mipmap, layer, face);
|
||||
}
|
||||
|
||||
glReadPixels(x, y, w, h, fmt.externalformat, fmt.type, imagedata->getData());
|
||||
glReadPixels(r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, imagedata->getData());
|
||||
|
||||
if (slice > 0)
|
||||
if (slice > 0 || mipmap > 0)
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
@@ -514,6 +521,21 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
||||
return imagedata;
|
||||
}
|
||||
|
||||
void Canvas::generateMipmaps()
|
||||
{
|
||||
if (getMipmapCount() == 1 || getMipmapMode() == MIPMAP_NONE)
|
||||
throw love::Exception("generateMipmaps can only be called on a Canvas which was created with mipmaps enabled.");
|
||||
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||
|
||||
if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
|
||||
glEnable(gltextype);
|
||||
|
||||
glGenerateMipmap(gltextype);
|
||||
}
|
||||
|
||||
PixelFormat Canvas::getSizedFormat(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
|
||||
@@ -53,18 +53,14 @@ public:
|
||||
void setDepthSampleMode(Optional<CompareMode> mode) override;
|
||||
ptrdiff_t getHandle() const override;
|
||||
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int x, int y, int w, int h) override;
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
||||
void generateMipmaps() override;
|
||||
|
||||
int getMSAA() const override
|
||||
{
|
||||
return actualSamples;
|
||||
}
|
||||
|
||||
int getRequestedMSAA() const override
|
||||
{
|
||||
return requestedSamples;
|
||||
}
|
||||
|
||||
ptrdiff_t getRenderTargetHandle() const override
|
||||
{
|
||||
return renderbuffer != 0 ? renderbuffer : texture;
|
||||
@@ -114,7 +110,6 @@ private:
|
||||
|
||||
GLenum status;
|
||||
|
||||
int requestedSamples;
|
||||
int actualSamples;
|
||||
|
||||
size_t textureMemory;
|
||||
|
||||
@@ -518,15 +518,17 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
for (int i = 0; i < ncanvases; i++)
|
||||
{
|
||||
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|
||||
|| rts.colors[i].slice != prevRTs.colors[i].slice)
|
||||
|| rts.colors[i].slice != prevRTs.colors[i].slice
|
||||
|| rts.colors[i].mipmap != prevRTs.colors[i].mipmap)
|
||||
{
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified && rts.depthStencil.canvas == prevRTs.depthStencil.canvas
|
||||
&& rts.depthStencil.slice == prevRTs.depthStencil.slice)
|
||||
if (!modified && (rts.depthStencil.canvas != prevRTs.depthStencil.canvas
|
||||
|| rts.depthStencil.slice != prevRTs.depthStencil.slice
|
||||
|| rts.depthStencil.mipmap != prevRTs.depthStencil.mipmap))
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
@@ -546,16 +548,23 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
if (isPixelFormatDepthStencil(firstformat))
|
||||
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (rts.colors[0].mipmap < 0 || rts.colors[0].mipmap >= firstcanvas->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", rts.colors[0].mipmap + 1);
|
||||
|
||||
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
|
||||
int pixelwidth = firstcanvas->getPixelWidth();
|
||||
int pixelheight = firstcanvas->getPixelHeight();
|
||||
int pixelwidth = firstcanvas->getPixelWidth(rts.colors[0].mipmap);
|
||||
int pixelheight = firstcanvas->getPixelHeight(rts.colors[0].mipmap);
|
||||
|
||||
for (int i = 1; i < ncanvases; i++)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.colors[i].canvas;
|
||||
PixelFormat format = c->getPixelFormat();
|
||||
int mip = rts.colors[i].mipmap;
|
||||
|
||||
if (c->getPixelWidth() != pixelwidth || c->getPixelHeight() != pixelheight)
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
|
||||
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (!multiformatsupported && format != firstformat)
|
||||
@@ -574,15 +583,19 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
if (rts.depthStencil.canvas != nullptr)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.depthStencil.canvas;
|
||||
int mip = rts.depthStencil.mipmap;
|
||||
|
||||
if (!isPixelFormatDepthStencil(c->getPixelFormat()))
|
||||
throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (c->getPixelWidth() != pixelwidth || c->getPixelHeight() != pixelheight)
|
||||
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||
throw love::Exception("All Canvases must have the same MSAA value.");
|
||||
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
}
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("setCanvas(...)");
|
||||
@@ -598,8 +611,8 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
if (state.scissor)
|
||||
setScissor(state.scissorRect);
|
||||
|
||||
int w = firstcanvas->getWidth();
|
||||
int h = firstcanvas->getHeight();
|
||||
int w = firstcanvas->getWidth(rts.colors[0].mipmap);
|
||||
int h = firstcanvas->getHeight(rts.colors[0].mipmap);
|
||||
projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h);
|
||||
|
||||
// Make sure the correct sRGB setting is used when drawing to the canvases.
|
||||
@@ -666,17 +679,19 @@ void Graphics::endPass()
|
||||
flushStreamDraws();
|
||||
|
||||
auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
|
||||
|
||||
// Discard the stencil buffer if we're using an internal cached one.
|
||||
if (rts.depthStencil.canvas.get() == nullptr)
|
||||
if (depthstencil == nullptr)
|
||||
discard({}, true);
|
||||
|
||||
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
|
||||
// don't have to worry about resolving to slices.
|
||||
if (rts.colors.size() > 0 && rts.colors[0].canvas->getMSAA() > 1)
|
||||
{
|
||||
int w = rts.colors[0].canvas->getPixelWidth();
|
||||
int h = rts.colors[0].canvas->getPixelHeight();
|
||||
int mip = rts.colors[0].mipmap;
|
||||
int w = rts.colors[0].canvas->getPixelWidth(mip);
|
||||
int h = rts.colors[0].canvas->getPixelHeight(mip);
|
||||
|
||||
for (int i = 0; i < (int) rts.colors.size(); i++)
|
||||
{
|
||||
@@ -695,6 +710,16 @@ void Graphics::endPass()
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &rt : rts.colors)
|
||||
{
|
||||
if (rt.canvas->getMipmapMode() == Canvas::MIPMAP_AUTO && rt.mipmap == 0)
|
||||
rt.canvas->generateMipmaps();
|
||||
}
|
||||
|
||||
int dsmipmap = rts.depthStencil.mipmap;
|
||||
if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAP_AUTO && dsmipmap == 0)
|
||||
depthstencil->generateMipmaps();
|
||||
}
|
||||
|
||||
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
|
||||
@@ -892,8 +917,9 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
|
||||
}
|
||||
else
|
||||
{
|
||||
int w = targets.colors[0].canvas->getPixelWidth();
|
||||
int h = targets.colors[0].canvas->getPixelHeight();
|
||||
int mip = targets.colors[0].mipmap;
|
||||
int w = targets.colors[0].canvas->getPixelWidth(mip);
|
||||
int h = targets.colors[0].canvas->getPixelHeight(mip);
|
||||
int msaa = targets.colors[0].canvas->getMSAA();
|
||||
int reqmsaa = targets.colors[0].canvas->getRequestedMSAA();
|
||||
|
||||
@@ -938,8 +964,9 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
|
||||
|
||||
int layer = textype == TEXTURE_CUBE ? 0 : rt.slice;
|
||||
int face = textype == TEXTURE_CUBE ? rt.slice : 0;
|
||||
int level = rt.mipmap;
|
||||
|
||||
gl.framebufferTexture(attachment, textype, handle, 0, layer, face);
|
||||
gl.framebufferTexture(attachment, textype, handle, level, layer, face);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,8 +33,6 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
float Image::maxMipmapSharpness = 0.0f;
|
||||
|
||||
Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
|
||||
: love::graphics::Image(Slices(textype), settings, false)
|
||||
, texture(0)
|
||||
@@ -98,8 +96,6 @@ void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
|
||||
|
||||
void Image::generateMipmaps()
|
||||
{
|
||||
// The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
|
||||
// have support for glGenerateMipmap.
|
||||
if (getMipmapCount() > 1 && !isCompressed() &&
|
||||
(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object))
|
||||
{
|
||||
@@ -270,9 +266,6 @@ bool Image::loadVolatile()
|
||||
filter.mipmap = FILTER_NONE;
|
||||
}
|
||||
|
||||
if (maxMipmapSharpness == 0.0f && GLAD_VERSION_1_4)
|
||||
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
|
||||
|
||||
glGenTextures(1, &texture);
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
@@ -418,17 +411,7 @@ ptrdiff_t Image::getHandle() const
|
||||
|
||||
void Image::setFilter(const Texture::Filter &f)
|
||||
{
|
||||
if (!validateFilter(f, getMipmapCount() > 1))
|
||||
{
|
||||
if (f.mipmap != FILTER_NONE && getMipmapCount() == 1)
|
||||
throw love::Exception("Non-mipmapped image cannot have mipmap filtering.");
|
||||
else
|
||||
throw love::Exception("Invalid texture filter.");
|
||||
}
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
|
||||
filter = f;
|
||||
Texture::setFilter(f);
|
||||
|
||||
if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
|
||||
{
|
||||
@@ -487,20 +470,22 @@ bool Image::setWrap(const Texture::Wrap &w)
|
||||
|
||||
bool Image::setMipmapSharpness(float sharpness)
|
||||
{
|
||||
// OpenGL ES doesn't support LOD bias via glTexParameter.
|
||||
if (!GLAD_VERSION_1_4)
|
||||
if (!gl.isSamplerLODBiasSupported())
|
||||
return false;
|
||||
|
||||
Graphics::flushStreamDrawsGlobal();
|
||||
|
||||
// LOD bias has the range (-maxbias, maxbias)
|
||||
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
|
||||
float maxbias = gl.getMaxLODBias();
|
||||
if (maxbias > 0.01f)
|
||||
maxbias -= 0.0f;
|
||||
|
||||
mipmapSharpness = std::min(std::max(sharpness, -maxbias), maxbias);
|
||||
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
// negative bias is sharper
|
||||
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||
glTexParameterf(gltextype, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||
glTexParameterf(gl.getGLTextureType(texType), GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,6 @@ private:
|
||||
|
||||
size_t textureMemorySize;
|
||||
|
||||
static float maxMipmapSharpness;
|
||||
|
||||
}; // Image
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -424,6 +424,11 @@ void OpenGL::initMaxValues()
|
||||
else
|
||||
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits);
|
||||
maxPointSize = limits[1];
|
||||
|
||||
if (isSamplerLODBiasSupported())
|
||||
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxLODBias);
|
||||
else
|
||||
maxLODBias = 0.0f;
|
||||
}
|
||||
|
||||
void OpenGL::createDefaultTexture()
|
||||
@@ -922,16 +927,10 @@ void OpenGL::deleteTexture(GLuint texture)
|
||||
|
||||
void OpenGL::setTextureFilter(TextureType target, graphics::Texture::Filter &f)
|
||||
{
|
||||
GLint gmin, gmag;
|
||||
GLint gmin = f.min == Texture::FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
|
||||
GLint gmag = f.mag == Texture::FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
|
||||
|
||||
if (f.mipmap == Texture::FILTER_NONE)
|
||||
{
|
||||
if (f.min == Texture::FILTER_NEAREST)
|
||||
gmin = GL_NEAREST;
|
||||
else // f.min == Texture::FILTER_LINEAR
|
||||
gmin = GL_LINEAR;
|
||||
}
|
||||
else
|
||||
if (f.mipmap != Texture::FILTER_NONE)
|
||||
{
|
||||
if (f.min == Texture::FILTER_NEAREST && f.mipmap == Texture::FILTER_NEAREST)
|
||||
gmin = GL_NEAREST_MIPMAP_NEAREST;
|
||||
@@ -945,17 +944,6 @@ void OpenGL::setTextureFilter(TextureType target, graphics::Texture::Filter &f)
|
||||
gmin = GL_LINEAR;
|
||||
}
|
||||
|
||||
switch (f.mag)
|
||||
{
|
||||
case Texture::FILTER_NEAREST:
|
||||
gmag = GL_NEAREST;
|
||||
break;
|
||||
case Texture::FILTER_LINEAR:
|
||||
default:
|
||||
gmag = GL_LINEAR;
|
||||
break;
|
||||
}
|
||||
|
||||
GLenum gltarget = getGLTextureType(target);
|
||||
|
||||
glTexParameteri(gltarget, GL_TEXTURE_MIN_FILTER, gmin);
|
||||
@@ -1138,6 +1126,11 @@ bool OpenGL::isDepthCompareSampleSupported() const
|
||||
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_shadow_samplers;
|
||||
}
|
||||
|
||||
bool OpenGL::isSamplerLODBiasSupported() const
|
||||
{
|
||||
return GLAD_VERSION_1_4;
|
||||
}
|
||||
|
||||
int OpenGL::getMax2DTextureSize() const
|
||||
{
|
||||
return std::max(max2DTextureSize, 1);
|
||||
@@ -1183,6 +1176,11 @@ float OpenGL::getMaxAnisotropy() const
|
||||
return maxAnisotropy;
|
||||
}
|
||||
|
||||
float OpenGL::getMaxLODBias() const
|
||||
{
|
||||
return maxLODBias;
|
||||
}
|
||||
|
||||
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
|
||||
{
|
||||
int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize);
|
||||
@@ -1748,10 +1746,14 @@ bool OpenGL::hasTextureFilteringSupport(PixelFormat pixelformat)
|
||||
{
|
||||
switch (pixelformat)
|
||||
{
|
||||
case PIXELFORMAT_R16F:
|
||||
case PIXELFORMAT_RG16F:
|
||||
case PIXELFORMAT_RGBA16F:
|
||||
return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear;
|
||||
case PIXELFORMAT_R32F:
|
||||
case PIXELFORMAT_RG32F:
|
||||
case PIXELFORMAT_RGBA32F:
|
||||
return GLAD_VERSION_1_1;
|
||||
return GLAD_VERSION_1_1 || GLAD_OES_texture_float_linear;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -328,6 +328,7 @@ public:
|
||||
bool isPixelShaderHighpSupported() const;
|
||||
bool isInstancingSupported() const;
|
||||
bool isDepthCompareSampleSupported() const;
|
||||
bool isSamplerLODBiasSupported() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported width or height of a texture.
|
||||
@@ -363,6 +364,8 @@ public:
|
||||
**/
|
||||
float getMaxAnisotropy() const;
|
||||
|
||||
float getMaxLODBias() const;
|
||||
|
||||
|
||||
void updateTextureMemorySize(size_t oldsize, size_t newsize);
|
||||
|
||||
@@ -407,6 +410,7 @@ private:
|
||||
|
||||
bool pixelShaderHighpSupported;
|
||||
float maxAnisotropy;
|
||||
float maxLODBias;
|
||||
int max2DTextureSize;
|
||||
int max3DTextureSize;
|
||||
int maxCubeTextureSize;
|
||||
|
||||
@@ -91,40 +91,58 @@ int w_Canvas_newImageData(lua_State *L)
|
||||
love::image::Image *image = luax_getmodule<love::image::Image>(L, love::image::Image::type);
|
||||
|
||||
int slice = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int w = canvas->getPixelWidth();
|
||||
int h = canvas->getPixelHeight();
|
||||
int mipmap = 0;
|
||||
|
||||
int startidx = 2;
|
||||
Rect rect;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.w = canvas->getPixelWidth();
|
||||
rect.h = canvas->getPixelHeight();
|
||||
|
||||
if (canvas->getTextureType() != TEXTURE_2D)
|
||||
if (!lua_isnoneornil(L, 2))
|
||||
{
|
||||
slice = (int) luaL_checknumber(L, startidx);
|
||||
startidx++;
|
||||
}
|
||||
rect.x = (int) luaL_checkinteger(L, 2);
|
||||
rect.y = (int) luaL_checkinteger(L, 3);
|
||||
rect.w = (int) luaL_checkinteger(L, 4);
|
||||
rect.h = (int) luaL_checkinteger(L, 5);
|
||||
|
||||
if (!lua_isnoneornil(L, startidx))
|
||||
{
|
||||
x = (int) luaL_checknumber(L, startidx + 0);
|
||||
y = (int) luaL_checknumber(L, startidx + 1);
|
||||
w = (int) luaL_checknumber(L, startidx + 2);
|
||||
h = (int) luaL_checknumber(L, startidx + 3);
|
||||
slice = (int) luaL_optinteger(L, 6, 1) - 1;
|
||||
mipmap = (int) luaL_optinteger(L, 7, 1) - 1;
|
||||
}
|
||||
|
||||
love::image::ImageData *img = nullptr;
|
||||
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, slice, x, y, w, h); });
|
||||
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, slice, mipmap, rect); });
|
||||
|
||||
luax_pushtype(L, img);
|
||||
img->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Canvas_generateMipmaps(lua_State *L)
|
||||
{
|
||||
Canvas *c = luax_checkcanvas(L, 1);
|
||||
luax_catchexcept(L, [&]() { c->generateMipmaps(); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Canvas_getMipmapMode(lua_State *L)
|
||||
{
|
||||
Canvas *c = luax_checkcanvas(L, 1);
|
||||
const char *str;
|
||||
if (!Canvas::getConstant(c->getMipmapMode(), str))
|
||||
return luaL_error(L, "Unknown mipmap mode.");
|
||||
|
||||
lua_pushstring(L, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_Canvas_functions[] =
|
||||
{
|
||||
{ "getMSAA", w_Canvas_getMSAA },
|
||||
{ "renderTo", w_Canvas_renderTo },
|
||||
{ "newImageData", w_Canvas_newImageData },
|
||||
{ "generateMipmaps", w_Canvas_generateMipmaps },
|
||||
{ "getMipmapMode", w_Canvas_getMipmapMode },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -251,6 +251,8 @@ static Graphics::RenderTarget checkRenderTarget(lua_State *L, int idx)
|
||||
else if (type == TEXTURE_CUBE)
|
||||
target.slice = luax_checkintflag(L, idx, "face") - 1;
|
||||
|
||||
target.mipmap = luax_intflag(L, idx, "mipmap", 1) - 1;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -308,10 +310,16 @@ int w_setCanvas(lua_State *L)
|
||||
|
||||
if (i == 1 && type != TEXTURE_2D)
|
||||
{
|
||||
target.slice = (int) luaL_checknumber(L, 2) - 1;
|
||||
target.slice = (int) luaL_checknumber(L, i + 1) - 1;
|
||||
target.mipmap = (int) luaL_optinteger(L, i + 2, 1) - 1;
|
||||
targets.colors.push_back(target);
|
||||
break;
|
||||
}
|
||||
else if (type == TEXTURE_2D && lua_isnumber(L, i + 1))
|
||||
{
|
||||
target.mipmap = (int) luaL_optinteger(L, i + 1, 1) - 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i > 1 && type != TEXTURE_2D)
|
||||
return luaL_error(L, "This variant of setCanvas only supports 2D texture types.");
|
||||
@@ -332,7 +340,7 @@ int w_setCanvas(lua_State *L)
|
||||
|
||||
static void pushRenderTarget(lua_State *L, const Graphics::RenderTarget &rt)
|
||||
{
|
||||
lua_createtable(L, 1, 1);
|
||||
lua_createtable(L, 1, 2);
|
||||
|
||||
luax_pushtype(L, rt.canvas);
|
||||
lua_rawseti(L, -2, 1);
|
||||
@@ -349,6 +357,9 @@ static void pushRenderTarget(lua_State *L, const Graphics::RenderTarget &rt)
|
||||
lua_pushnumber(L, rt.slice + 1);
|
||||
lua_setfield(L, -2, "face");
|
||||
}
|
||||
|
||||
lua_pushnumber(L, rt.mipmap + 1);
|
||||
lua_setfield(L, -2, "mipmap");
|
||||
}
|
||||
|
||||
int w_getCanvas(lua_State *L)
|
||||
@@ -362,17 +373,21 @@ int w_getCanvas(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool hasNon2DTextureType = false;
|
||||
for (const auto &rt : targets.colors)
|
||||
bool shouldUseTablesVariant = targets.depthStencil.canvas != nullptr;
|
||||
|
||||
if (!shouldUseTablesVariant)
|
||||
{
|
||||
if (rt.canvas->getTextureType() != TEXTURE_2D)
|
||||
for (const auto &rt : targets.colors)
|
||||
{
|
||||
hasNon2DTextureType = true;
|
||||
break;
|
||||
if (rt.mipmap != 0 || rt.canvas->getTextureType() != TEXTURE_2D)
|
||||
{
|
||||
shouldUseTablesVariant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNon2DTextureType || targets.depthStencil.canvas != nullptr)
|
||||
if (shouldUseTablesVariant)
|
||||
{
|
||||
lua_createtable(L, ntargets, 0);
|
||||
|
||||
@@ -1063,7 +1078,7 @@ int w_newCanvas(lua_State *L)
|
||||
if (!lua_isnoneornil(L, startidx))
|
||||
{
|
||||
settings.pixeldensity = (float) luax_numberflag(L, startidx, "pixeldensity", settings.pixeldensity);
|
||||
settings.msaa = luax_intflag(L, startidx, "msaa", 0);
|
||||
settings.msaa = luax_intflag(L, startidx, "msaa", settings.msaa);
|
||||
|
||||
lua_getfield(L, startidx, "format");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
@@ -1087,10 +1102,19 @@ int w_newCanvas(lua_State *L)
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
luaL_checktype(L, -1, LUA_TBOOLEAN);
|
||||
settings.readable.set = true;
|
||||
settings.readable.hasValue = true;
|
||||
settings.readable.value = luax_toboolean(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, startidx, "mipmaps");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
const char *str = luaL_checkstring(L, -1);
|
||||
if (!Canvas::getConstant(str, settings.mipmaps))
|
||||
return luaL_error(L, "Invalid Canvas mipmap mode: %s", str);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
Canvas *canvas = nullptr;
|
||||
|
||||
@@ -40,32 +40,48 @@ int w_Texture_getTextureType(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int w__optMipmap(lua_State *L, Texture *t, int idx)
|
||||
{
|
||||
int mipmap = 0;
|
||||
|
||||
if (!lua_isnoneornil(L, idx))
|
||||
{
|
||||
mipmap = (int) luaL_checkinteger(L, idx) - 1;
|
||||
|
||||
if (mipmap < 0 || mipmap >= t->getMipmapCount())
|
||||
luaL_error(L, "Invalid mipmap index: %d", mipmap + 1);
|
||||
}
|
||||
|
||||
return mipmap;
|
||||
}
|
||||
|
||||
int w_Texture_getWidth(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getWidth());
|
||||
lua_pushnumber(L, t->getWidth(w__optMipmap(L, t, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_getHeight(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getHeight());
|
||||
lua_pushnumber(L, t->getHeight(w__optMipmap(L, t, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_getDimensions(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getWidth());
|
||||
lua_pushnumber(L, t->getHeight());
|
||||
int mipmap = w__optMipmap(L, t, 2);
|
||||
lua_pushnumber(L, t->getWidth(mipmap));
|
||||
lua_pushnumber(L, t->getHeight(mipmap));
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_Texture_getDepth(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getDepth());
|
||||
lua_pushnumber(L, t->getDepth(w__optMipmap(L, t, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -86,22 +102,23 @@ int w_Texture_getMipmapCount(lua_State *L)
|
||||
int w_Texture_getPixelWidth(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getPixelWidth());
|
||||
lua_pushnumber(L, t->getPixelWidth(w__optMipmap(L, t, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_getPixelHeight(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getPixelHeight());
|
||||
lua_pushnumber(L, t->getPixelHeight(w__optMipmap(L, t, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_getPixelDimensions(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
lua_pushnumber(L, t->getPixelWidth());
|
||||
lua_pushnumber(L, t->getPixelHeight());
|
||||
int mipmap = w__optMipmap(L, t, 2);
|
||||
lua_pushnumber(L, t->getPixelWidth(mipmap));
|
||||
lua_pushnumber(L, t->getPixelHeight(mipmap));
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user