Merge minor into dynamiccore2

--HG--
branch : dynamiccore2
This commit is contained in:
Bart van Strien
2016-11-26 22:19:12 +01:00
61 changed files with 4909 additions and 1720 deletions
+3
View File
@@ -260,6 +260,8 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
{ "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS },
{ "clampzero", FEATURE_CLAMP_ZERO },
{ "lighten", FEATURE_LIGHTEN },
{ "fullnpot", FEATURE_FULL_NPOT },
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
};
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
@@ -270,6 +272,7 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
{ "texturesize", LIMIT_TEXTURE_SIZE },
{ "multicanvas", LIMIT_MULTI_CANVAS },
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
{ "anisotropy", LIMIT_ANISOTROPY },
};
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
+16 -1
View File
@@ -34,6 +34,8 @@ namespace love
namespace graphics
{
const int MAX_COLOR_RENDER_TARGETS = 16;
/**
* Globally sets whether gamma correction is enabled. Ideally this should be set
* prior to using any Graphics module function.
@@ -59,6 +61,14 @@ void gammaCorrectColor(Colorf &c);
**/
void unGammaCorrectColor(Colorf &c);
class RenderOutsidePassException : public love::Exception
{
public:
RenderOutsidePassException()
: Exception("Cannot draw outside of a render pass!")
{}
};
class Graphics : public Module
{
public:
@@ -143,6 +153,8 @@ public:
FEATURE_MULTI_CANVAS_FORMATS,
FEATURE_CLAMP_ZERO,
FEATURE_LIGHTEN,
FEATURE_FULL_NPOT,
FEATURE_PIXEL_SHADER_HIGHP,
FEATURE_MAX_ENUM
};
@@ -159,6 +171,7 @@ public:
LIMIT_TEXTURE_SIZE,
LIMIT_MULTI_CANVAS,
LIMIT_CANVAS_MSAA,
LIMIT_ANISOTROPY,
LIMIT_MAX_ENUM
};
@@ -180,7 +193,7 @@ public:
struct Stats
{
int drawCalls;
int canvasSwitches;
int renderPasses;
int shaderSwitches;
int canvases;
int images;
@@ -259,6 +272,8 @@ public:
**/
virtual bool isActive() const = 0;
virtual bool isPassActive() const = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
+56 -409
View File
@@ -36,11 +36,10 @@ namespace opengl
static GLenum createFBO(GLuint &framebuffer, GLuint texture)
{
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
if (texture != 0)
{
@@ -53,14 +52,19 @@ static GLenum createFBO(GLuint &framebuffer, GLuint texture)
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind framebuffer
gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
return status;
}
static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
static bool createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
{
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
// Temporary FBO used to clear the renderbuffer.
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
@@ -73,7 +77,7 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE)
if (status == GL_FRAMEBUFFER_COMPLETE && samples > 1)
{
// Initialize the buffer to transparent black.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -83,23 +87,22 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform
{
glDeleteRenderbuffers(1, &buffer);
buffer = 0;
samples = 0;
}
return status;
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo);
return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
}
love::Type Canvas::type("Canvas", &Texture::type);
Canvas *Canvas::current = nullptr;
OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
bool Canvas::screenHasSRGB = false;
int Canvas::canvasCount = 0;
Canvas::Canvas(int width, int height, Format format, int msaa)
: fbo(0)
, resolve_fbo(0)
, texture(0)
, msaa_buffer(0)
, depth_stencil(0)
, format(format)
, requested_samples(msaa)
, actual_samples(0)
@@ -144,65 +147,18 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
Canvas::~Canvas()
{
--canvasCount;
// reset framebuffer if still using this one
if (current == this)
stopGrab();
unloadVolatile();
}
bool Canvas::createMSAAFBO(GLenum internalformat)
{
actual_samples = requested_samples;
if (actual_samples <= 1)
{
actual_samples = 0;
return false;
}
// Create our FBO without a texture.
status = createFBO(fbo, 0);
GLuint previous = gl.getDefaultFBO();
if (current != this)
{
if (current != nullptr)
previous = current->fbo;
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
}
// Create and attach the MSAA buffer for our FBO.
status = createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer);
// Create the FBO used for the MSAA resolve, and attach the texture.
if (status == GL_FRAMEBUFFER_COMPLETE)
status = createFBO(resolve_fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
// Clean up.
glDeleteFramebuffers(1, &fbo);
glDeleteFramebuffers(1, &resolve_fbo);
glDeleteRenderbuffers(1, &msaa_buffer);
fbo = msaa_buffer = resolve_fbo = 0;
actual_samples = 0;
}
if (current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, previous);
return status == GL_FRAMEBUFFER_COMPLETE;
}
bool Canvas::loadVolatile()
{
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Canvas load");
fbo = depth_stencil = texture = 0;
resolve_fbo = msaa_buffer = 0;
fbo = texture = 0;
msaa_buffer = 0;
status = GL_FRAMEBUFFER_COMPLETE;
// glTexImage2D is guaranteed to error in this case.
@@ -240,8 +196,8 @@ bool Canvas::loadVolatile()
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0,
externalformat, textype, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, externalformat,
textype, nullptr);
if (glGetError() != GL_NO_ERROR)
{
@@ -251,24 +207,27 @@ bool Canvas::loadVolatile()
return false;
}
// Try to create a MSAA FBO if requested. On failure (or no requested MSAA),
// fall back to a regular FBO.
if (!createMSAAFBO(internalformat))
status = createFBO(fbo, texture);
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (fbo != 0)
{
glDeleteFramebuffers(1, &fbo);
fbo = 0;
}
{
if (fbo != 0)
{
gl.deleteFramebuffer(fbo);
fbo = 0;
}
return false;
}
}
actual_samples = requested_samples == 1 ? 0 : requested_samples;
if (actual_samples > 0 && !createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer))
actual_samples = 0;
size_t prevmemsize = texture_memory;
texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8;
texture_memory = ((getFormatBitsPerPixel(format) * width) / 8) * height;
if (msaa_buffer != 0)
texture_memory += (texture_memory * actual_samples);
@@ -279,33 +238,35 @@ bool Canvas::loadVolatile()
void Canvas::unloadVolatile()
{
glDeleteFramebuffers(1, &fbo);
glDeleteFramebuffers(1, &resolve_fbo);
if (fbo != 0)
gl.deleteFramebuffer(fbo);
glDeleteRenderbuffers(1, &depth_stencil);
glDeleteRenderbuffers(1, &msaa_buffer);
if (msaa_buffer != 0)
glDeleteRenderbuffers(1, &msaa_buffer);
gl.deleteTexture(texture);
if (texture != 0)
gl.deleteTexture(texture);
fbo = 0;
resolve_fbo = 0;
depth_stencil = 0;
msaa_buffer = 0;
texture = 0;
attachedCanvases.clear();
gl.updateTextureMemorySize(texture_memory, 0);
texture_memory = 0;
}
void Canvas::drawv(const Matrix4 &t, const Vertex *v)
{
// FIXME: This doesn't handle cases where the Canvas is used as a texture
// in a SpriteBatch, Mesh, or ParticleSystem, or when the Canvas is used in
// a shader as a non-default texture.
if (Canvas::current == this)
throw love::Exception("Cannot draw a Canvas to itself.");
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
{
const PassInfo &info = gfx->getActivePass();
for (const auto &attachment : info.colorAttachments)
{
if (attachment.canvas == this)
throw love::Exception("Cannot render a Canvas to itself!");
}
}
OpenGL::TempDebugGroup debuggroup("Canvas draw");
@@ -378,320 +339,6 @@ const void *Canvas::getHandle() const
return &texture;
}
void Canvas::setupGrab()
{
// already grabbing
if (current == this)
return;
// cleanup after previous Canvas
if (current != nullptr)
{
systemViewport = current->systemViewport;
current->stopGrab(true);
}
else
systemViewport = gl.getViewport();
// indicate we are using this Canvas.
current = this;
// bind the framebuffer object.
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
gl.setViewport({0, 0, width, height});
// Set up the projection matrix
gl.matrices.projection.push_back(Matrix4::ortho(0.0, (float) width, 0.0, (float) height));
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
{
// Whether the new canvas list is different from the old one.
// A more thorough check is done below.
bool canvaseschanged = canvases.size() != attachedCanvases.size();
bool hasSRGBcanvas = getSizedFormat(format) == FORMAT_SRGB;
if (canvases.size() > 0)
{
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1);
if (actual_samples != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
}
bool multiformatsupported = isMultiFormatMultiCanvasSupported();
for (size_t i = 0; i < canvases.size(); i++)
{
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
throw love::Exception("All canvases must have the same dimensions.");
Format otherformat = canvases[i]->getTextureFormat();
if (otherformat != format && !multiformatsupported)
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
if (canvases[i]->getMSAA() != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
if (getSizedFormat(otherformat) == FORMAT_SRGB)
hasSRGBcanvas = true;
}
OpenGL::TempDebugGroup debuggroup("Canvas set");
setupGrab();
// Make sure the correct sRGB setting is used when drawing to the canvases.
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
if (hasSRGBcanvas && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!hasSRGBcanvas && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
// Don't attach anything if there's nothing to change.
if (!canvaseschanged)
return;
// Attach the canvas textures to the active FBO and set up MRTs.
std::vector<GLenum> drawbuffers;
drawbuffers.reserve(canvases.size() + 1);
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
// Attach the canvas textures to the currently bound framebuffer.
for (int i = 0; i < (int) canvases.size(); i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
GL_TEXTURE_2D, *(GLuint *) canvases[i]->getHandle(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
}
// set up multiple render targets
glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
// We want to avoid reference cycles, so we don't retain the attached
// Canvases here. The code in Graphics::setCanvas retains them.
attachedCanvases = canvases;
}
void Canvas::startGrab()
{
OpenGL::TempDebugGroup debuggroup("Canvas set");
setupGrab();
// Make sure the correct sRGB setting is used when drawing to the canvas.
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
bool isSRGB = getSizedFormat(format) == FORMAT_SRGB;
if (isSRGB && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!isSRGB && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
if (attachedCanvases.size() > 0)
{
// Make sure the FBO is only using a single draw buffer.
// GLES3 only has glDrawBuffers, so we avoid using glDrawBuffer.
const GLenum buffers[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, buffers);
attachedCanvases.clear();
}
}
void Canvas::stopGrab(bool switchingToOtherCanvas)
{
// i am not grabbing. leave me alone
if (current != this)
return;
OpenGL::TempDebugGroup debuggroup("Canvas un-set");
// Make sure the canvas texture is up to date if we're using MSAA.
resolveMSAA(false);
if (gl.matrices.projection.size() > 1)
gl.matrices.projection.pop_back();
if (!switchingToOtherCanvas)
{
// bind system framebuffer.
gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
current = nullptr;
gl.setViewport(systemViewport);
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
{
if (screenHasSRGB && !gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(true);
else if (!screenHasSRGB && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
}
}
bool Canvas::checkCreateStencil()
{
// Do nothing if we've already created the stencil buffer.
if (depth_stencil != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Canvas create stencil");
if (current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
GLenum format = GL_STENCIL_INDEX8;
std::vector<GLenum> attachments = {GL_STENCIL_ATTACHMENT};
// Prefer a combined depth/stencil buffer.
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
{
format = GL_DEPTH24_STENCIL8;
attachments = {GL_DEPTH_STENCIL_ATTACHMENT};
}
else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
{
format = GL_DEPTH24_STENCIL8;
attachments = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
}
glGenRenderbuffers(1, &depth_stencil);
glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
if (requested_samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, requested_samples, format, width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
// Attach the buffer to the framebuffer object.
for (GLenum attachment : attachments)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, depth_stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
// We don't want the stencil buffer filled with garbage.
if (success)
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
else
{
glDeleteRenderbuffers(1, &depth_stencil);
depth_stencil = 0;
}
if (current && current != this)
gl.bindFramebuffer(GL_FRAMEBUFFER, current->fbo);
else if (!current)
gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
return success;
}
love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, int y, int w, int h)
{
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > width || (y + h) > height)
throw love::Exception("Invalid ImageData rectangle dimensions.");
GLenum datatype = GL_UNSIGNED_BYTE;
image::ImageData::Format imageformat = image::ImageData::FORMAT_RGBA8;
switch (getSizedFormat(format))
{
case FORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES
datatype = GL_UNSIGNED_SHORT;
imageformat = image::ImageData::FORMAT_RGBA16;
break;
case FORMAT_R16F:
case FORMAT_RG16F:
case FORMAT_RGBA16F:
case FORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES
datatype = GL_HALF_FLOAT;
imageformat = image::ImageData::FORMAT_RGBA16F;
break;
case FORMAT_R32F:
case FORMAT_RG32F:
case FORMAT_RGBA32F:
datatype = GL_FLOAT;
imageformat = image::ImageData::FORMAT_RGBA32F;
break;
default:
break;
}
size_t size = w * h * image::ImageData::getPixelSize(imageformat);
uint8 *pixels = nullptr;
try
{
pixels = new uint8[size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Make sure the canvas texture is up to date if we're using MSAA.
if (current == this)
resolveMSAA(false);
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (resolve_fbo != 0)
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
glReadPixels(x, y, w, h, GL_RGBA, datatype, pixels);
GLuint prevfbo = current ? current->fbo : gl.getDefaultFBO();
gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
// The new ImageData now owns the pixel data, so we don't delete it here.
return image->newImageData(w, h, imageformat, pixels, true);
}
bool Canvas::resolveMSAA(bool restoreprev)
{
if (resolve_fbo == 0 || msaa_buffer == 0)
return false;
OpenGL::TempDebugGroup debuggroup("Canvas MSAA resolve");
GLint w = width;
GLint h = height;
// Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
// For many of the MSAA extensions that add suffixes to the functions, we
// assign function pointers in OpenGL.cpp so we can call the core functions.
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
if (GLAD_APPLE_framebuffer_multisample)
glResolveMultisampleFramebufferAPPLE();
else
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (restoreprev)
{
GLuint fbo = current ? current->fbo : gl.getDefaultFBO();
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
}
return true;
}
Canvas::Format Canvas::getSizedFormat(Canvas::Format format)
{
switch (format)
@@ -950,7 +597,7 @@ bool Canvas::isFormatSupported(Canvas::Format format)
GLuint fbo = 0;
supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
glDeleteFramebuffers(1, &fbo);
gl.deleteFramebuffer(fbo);
gl.deleteTexture(texture);
+15 -37
View File
@@ -84,26 +84,6 @@ public:
bool setWrap(const Texture::Wrap &w) override;
const void *getHandle() const override;
/**
* @param canvases A list of other canvases to temporarily attach to this one,
* to allow drawing to multiple canvases at once.
**/
void startGrab(const std::vector<Canvas *> &canvases);
void startGrab();
void stopGrab(bool switchingToOtherCanvas = false);
/**
* Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
**/
bool checkCreateStencil();
love::image::ImageData *newImageData(love::image::Image *image, int x, int y, int w, int h);
inline const std::vector<Canvas *> &getAttachedCanvases() const
{
return attachedCanvases;
}
inline GLenum getStatus() const
{
return status;
@@ -119,19 +99,26 @@ public:
return actual_samples;
}
inline int getRequestedMSAA() const
{
return requested_samples;
}
inline ptrdiff_t getMSAAHandle() const
{
return msaa_buffer;
}
inline GLuint getFBO() const
{
return fbo;
}
static Format getSizedFormat(Format format);
static bool isSupported();
static bool isMultiFormatMultiCanvasSupported();
static bool isFormatSupported(Format format);
static Canvas *current;
// The viewport dimensions of the system (default) framebuffer.
static OpenGL::Viewport systemViewport;
// Whether the main screen should have linear -> sRGB conversions enabled.
static bool screenHasSRGB;
static int canvasCount;
static bool getConstant(const char *in, Format &out);
@@ -139,29 +126,20 @@ public:
private:
void setupGrab();
bool createMSAAFBO(GLenum internalformat);
bool resolveMSAA(bool restoreprev);
void drawv(const Matrix4 &t, const Vertex *v);
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
static size_t getFormatBitsPerPixel(Format format);
GLuint fbo;
GLuint resolve_fbo;
GLuint texture;
GLuint msaa_buffer;
GLuint depth_stencil;
Format format;
GLenum status;
std::vector<Canvas *> attachedCanvases;
int requested_samples;
int actual_samples;
File diff suppressed because it is too large Load Diff
+105 -38
View File
@@ -24,6 +24,7 @@
// STD
#include <stack>
#include <vector>
#include <unordered_map>
// OpenGL
#include "OpenGL.h"
@@ -35,10 +36,10 @@
#include "image/Image.h"
#include "image/ImageData.h"
#include "window/Window.h"
#include "video/VideoStream.h"
#include "math/Transform.h"
#include "Font.h"
#include "Image.h"
#include "graphics/Quad.h"
@@ -53,21 +54,63 @@
namespace love
{
class Reference;
namespace graphics
{
namespace opengl
{
struct PassInfo
{
enum BeginAction
{
BEGIN_LOAD,
BEGIN_CLEAR,
BEGIN_DISCARD,
};
enum EndAction
{
END_STORE,
END_DISCARD,
};
struct ColorAttachment
{
Canvas *canvas = nullptr;
Colorf clearColor = Colorf(0.0f, 0.0f, 0.0f, 0.0f);
BeginAction beginAction = BEGIN_LOAD;
};
ColorAttachment colorAttachments[MAX_COLOR_RENDER_TARGETS];
int colorAttachmentCount = 0;
bool stencil = false;
bool addColorAttachment(const ColorAttachment &attachment)
{
if (colorAttachmentCount + 1 < MAX_COLOR_RENDER_TARGETS)
{
colorAttachments[colorAttachmentCount++] = attachment;
return true;
}
return false;
}
};
class Graphics : public love::graphics::Graphics
{
public:
struct OptionalColorf
{
float r, g, b, a;
bool enabled;
typedef void (*ScreenshotCallback)(love::image::ImageData *i, Reference *ref, void *ud);
Colorf toColor() const { return Colorf(r, g, b, a); }
struct ScreenshotInfo
{
ScreenshotCallback callback;
Reference *ref;
};
Graphics();
@@ -92,25 +135,19 @@ public:
**/
void reset();
/**
* Clears the screen to a specific color.
**/
void clear(Colorf c);
void beginPass(PassInfo::BeginAction beginAction, Colorf clearColor);
void beginPass(const PassInfo &info);
/**
* Clears each active canvas to a different color.
**/
void clear(const std::vector<OptionalColorf> &colors);
void endPass();
void endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData);
/**
* Discards the contents of the screen.
**/
void discard(const std::vector<bool> &colorbuffers, bool stencil);
const PassInfo &getActivePass() const;
virtual bool isPassActive() const;
/**
* Flips buffers. (Rendered geometry is presented on screen).
**/
void present();
void present(void *screenshotCallbackData);
/**
* Gets the width of the current graphics viewport.
@@ -122,6 +159,9 @@ public:
**/
int getHeight() const;
int getPassWidth() const;
int getPassHeight() const;
/**
* True if a graphics viewport is set.
**/
@@ -231,13 +271,6 @@ public:
Shader *getShader() const;
void setCanvas(Canvas *canvas);
void setCanvas(const std::vector<Canvas *> &canvases);
void setCanvas(const std::vector<StrongRef<Canvas>> &canvases);
void setCanvas();
std::vector<Canvas *> getCanvas() const;
/**
* Sets the enabled color components when rendering.
**/
@@ -330,6 +363,9 @@ public:
**/
bool isWireframe() const;
void draw(Drawable *drawable, const Matrix4 &m);
void drawq(Texture *texture, Quad *quad, const Matrix4 &m);
/**
* Draws text at the specified coordinates
**/
@@ -422,12 +458,7 @@ public:
**/
void polygon(DrawMode mode, const float *coords, size_t count);
/**
* Creates a screenshot of the view and saves it to the default folder.
* @param image The love.image module.
* @param copyAlpha If the alpha channel should be copied or set to full opacity (1.0).
**/
love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true);
void captureScreenshot(const ScreenshotInfo &info);
/**
* Returns system-dependent renderer information.
@@ -459,6 +490,10 @@ public:
void translate(float x, float y);
void shear(float kx, float ky);
void origin();
void applyTransform(love::math::Transform *transform);
void replaceTransform(love::math::Transform *transform);
Vector transformPoint(Vector point);
Vector inverseTransformPoint(Vector point);
@@ -488,8 +523,6 @@ private:
StrongRef<Font> font;
StrongRef<Shader> shader;
std::vector<StrongRef<Canvas>> canvases;
ColorMask colorMask = ColorMask(true, true, true, true);
bool wireframe = false;
@@ -500,18 +533,46 @@ private:
float defaultMipmapSharpness = 0.0f;
};
struct CurrentPass
{
PassInfo info;
bool active = false;
};
struct PassBufferInfo
{
bool stencil;
Canvas *canvases[MAX_COLOR_RENDER_TARGETS];
};
struct CachedRenderbuffer
{
int w;
int h;
int samples;
GLenum attachments[2];
GLuint renderbuffer;
};
void restoreState(const DisplayState &s);
void restoreStateChecked(const DisplayState &s);
void bindCachedFBOForPass(const PassInfo &pass);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
GLuint attachCachedStencilBuffer(int w, int h, int samples);
void checkSetDefaultFont();
int calculateEllipsePoints(float rx, float ry) const;
StrongRef<love::window::Window> currentWindow;
StrongRef<Font> defaultFont;
std::vector<double> pixelSizeStack; // stores current size of a pixel (needed for line drawing)
std::vector<double> pixelScaleStack;
std::vector<ScreenshotInfo> pendingScreenshotCallbacks;
std::unordered_map<uint32, GLuint> framebufferObjects;
std::vector<CachedRenderbuffer> stencilBuffers;
QuadIndices *quadIndices;
@@ -520,11 +581,17 @@ private:
bool created;
bool active;
bool canCaptureScreenshot;
CurrentPass currentPass;
bool writingToStencil;
std::vector<DisplayState> states;
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
int renderPassCount;
static const size_t MAX_USER_STACK_DEPTH = 64;
}; // Graphics
+3
View File
@@ -283,6 +283,9 @@ void Image::loadFromImageData()
bool Image::loadVolatile()
{
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Image load");
if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB))
+21 -3
View File
@@ -311,7 +311,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
return it->second.enabled;
}
void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname)
{
if (mesh != this)
{
@@ -333,10 +333,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
newattrib.mesh = mesh;
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
newattrib.index = mesh->getAttributeIndex(name);
newattrib.index = mesh->getAttributeIndex(attachname);
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
if (newattrib.mesh != this)
newattrib.mesh->retain();
@@ -347,6 +347,24 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
oldattrib.mesh->release();
}
bool Mesh::detachAttribute(const std::string &name)
{
auto it = attachedAttributes.find(name);
if (it != attachedAttributes.end() && it->second.mesh != this)
{
it->second.mesh->release();
attachedAttributes.erase(it);
if (getAttributeIndex(name) != -1)
attachAttribute(name, this, name);
return true;
}
return false;
}
void *Mesh::mapVertexData()
{
return vbo->map();
+2 -1
View File
@@ -140,7 +140,8 @@ public:
* Attaches a vertex attribute from another Mesh to this one. The attribute
* will be used when drawing this Mesh.
**/
void attachAttribute(const std::string &name, Mesh *mesh);
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname);
bool detachAttribute(const std::string &name);
void *mapVertexData();
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
+109 -16
View File
@@ -23,7 +23,6 @@
#include "OpenGL.h"
#include "Shader.h"
#include "Canvas.h"
#include "common/Exception.h"
// C++
@@ -66,6 +65,7 @@ static void *LOVEGetProcAddress(const char *name)
OpenGL::OpenGL()
: stats()
, contextInitialized(false)
, pixelShaderHighpSupported(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, maxRenderTargets(1)
@@ -75,7 +75,6 @@ OpenGL::OpenGL()
, state()
{
matrices.transform.reserve(10);
matrices.projection.reserve(2);
}
bool OpenGL::initContext()
@@ -135,6 +134,10 @@ void OpenGL::setupContext()
else
state.pointSize = 1.0f;
for (int i = 0; i < 2; i++)
state.boundFramebuffers[i] = std::numeric_limits<GLuint>::max();
bindFramebuffer(FRAMEBUFFER_ALL, getDefaultFBO());
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|| GLAD_EXT_sRGB_write_control)
{
@@ -266,6 +269,16 @@ void OpenGL::initOpenGLFunctions()
void OpenGL::initMaxValues()
{
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
{
GLint range = 0;
GLint precision = 0;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, &range, &precision);
pixelShaderHighpSupported = range > 0;
}
else
pixelShaderHighpSupported = true;
// We'll need this value to clamp anisotropy.
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
@@ -283,7 +296,7 @@ void OpenGL::initMaxValues()
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
}
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
maxRenderTargets = std::max(std::min(maxattachments, maxdrawbuffers), 1);
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|| GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample
@@ -304,10 +317,9 @@ void OpenGL::initMaxValues()
void OpenGL::initMatrices()
{
matrices.transform.clear();
matrices.projection.clear();
matrices.transform.push_back(Matrix4());
matrices.projection.push_back(Matrix4());
matrices.projection = Matrix4();
}
void OpenGL::createDefaultTexture()
@@ -361,7 +373,7 @@ void OpenGL::prepareDraw()
// because uniform uploads can be significantly slower than glLoadMatrix.
if (GLAD_VERSION_1_0)
{
const Matrix4 &curproj = matrices.projection.back();
const Matrix4 &curproj = matrices.projection;
const Matrix4 &curxform = matrices.transform.back();
const Matrix4 &lastproj = state.lastProjectionMatrix;
@@ -374,7 +386,7 @@ void OpenGL::prepareDraw()
glLoadMatrixf(curproj.getElements());
glMatrixMode(GL_MODELVIEW);
state.lastProjectionMatrix = matrices.projection.back();
state.lastProjectionMatrix = matrices.projection;
}
// Same with the transform matrix.
@@ -463,7 +475,7 @@ void OpenGL::useVertexAttribArrays(uint32 arraybits)
glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
}
void OpenGL::setViewport(const OpenGL::Viewport &v)
void OpenGL::setViewport(const OpenGL::Viewport &v, bool canvasActive)
{
glViewport(v.x, v.y, v.w, v.h);
state.viewport = v;
@@ -471,7 +483,7 @@ void OpenGL::setViewport(const OpenGL::Viewport &v)
// glScissor starts from the lower left, so we compensate when setting the
// scissor. When the viewport is changed, we need to manually update the
// scissor again.
setScissor(state.scissor);
setScissor(state.scissor, canvasActive);
}
OpenGL::Viewport OpenGL::getViewport() const
@@ -479,9 +491,9 @@ OpenGL::Viewport OpenGL::getViewport() const
return state.viewport;
}
void OpenGL::setScissor(const OpenGL::Viewport &v)
void OpenGL::setScissor(const OpenGL::Viewport &v, bool canvasActive)
{
if (Canvas::current)
if (canvasActive)
glScissor(v.x, v.y, v.w, v.h);
else
{
@@ -526,12 +538,53 @@ bool OpenGL::hasFramebufferSRGB() const
return state.framebufferSRGBEnabled;
}
void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer)
void OpenGL::bindFramebuffer(FramebufferTarget target, GLuint framebuffer)
{
glBindFramebuffer(target, framebuffer);
bool bindingmodified = false;
if (target == GL_FRAMEBUFFER)
++stats.framebufferBinds;
if ((target & FRAMEBUFFER_DRAW) && state.boundFramebuffers[0] != framebuffer)
{
bindingmodified = true;
state.boundFramebuffers[0] = framebuffer;
}
if ((target & FRAMEBUFFER_READ) && state.boundFramebuffers[1] != framebuffer)
{
bindingmodified = true;
state.boundFramebuffers[1] = framebuffer;
}
if (bindingmodified)
{
GLenum gltarget = GL_FRAMEBUFFER;
if (target == FRAMEBUFFER_DRAW)
gltarget = GL_DRAW_FRAMEBUFFER;
else if (target == FRAMEBUFFER_READ)
gltarget = GL_READ_FRAMEBUFFER;
glBindFramebuffer(gltarget, framebuffer);
}
}
GLenum OpenGL::getFramebuffer(FramebufferTarget target) const
{
if (target & FRAMEBUFFER_DRAW)
return state.boundFramebuffers[0];
else if (target & FRAMEBUFFER_READ)
return state.boundFramebuffers[1];
else
return 0;
}
void OpenGL::deleteFramebuffer(GLuint framebuffer)
{
glDeleteFramebuffers(1, &framebuffer);
for (int i = 0; i < 2; i++)
{
if (state.boundFramebuffers[i] == framebuffer)
state.boundFramebuffers[i] = 0;
}
}
void OpenGL::useProgram(GLuint program)
@@ -673,6 +726,11 @@ bool OpenGL::isClampZeroTextureWrapSupported() const
return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp;
}
bool OpenGL::isPixelShaderHighpSupported() const
{
return pixelShaderHighpSupported;
}
int OpenGL::getMaxTextureSize() const
{
return maxTextureSize;
@@ -680,7 +738,7 @@ int OpenGL::getMaxTextureSize() const
int OpenGL::getMaxRenderTargets() const
{
return maxRenderTargets;
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
}
int OpenGL::getMaxRenderbufferSamples() const
@@ -698,6 +756,11 @@ float OpenGL::getMaxPointSize() const
return maxPointSize;
}
float OpenGL::getMaxAnisotropy() const
{
return maxAnisotropy;
}
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
{
int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize);
@@ -739,6 +802,36 @@ const char *OpenGL::errorString(GLenum errorcode)
return text;
}
const char *OpenGL::framebufferStatusString(GLenum status)
{
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
return "complete (success)";
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
return "Texture format cannot be rendered to on this system.";
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
return "Error in graphics driver (missing render texture attachment)";
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
return "Error in graphics driver (incomplete draw buffer)";
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
return "Error in graphics driver (incomplete read buffer)";
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
return "Canvas with the specified MSAA count cannot be rendered to on this system.";
case GL_FRAMEBUFFER_UNSUPPORTED:
return "Renderable textures are unsupported";
default:
break;
}
static char text[64] = {};
memset(text, 0, sizeof(text));
sprintf(text, "0x%x", status);
return text;
}
const char *OpenGL::debugSeverityString(GLenum severity)
{
switch (severity)
+24 -5
View File
@@ -104,6 +104,13 @@ public:
VENDOR_UNKNOWN
};
enum FramebufferTarget
{
FRAMEBUFFER_READ = (1 << 0),
FRAMEBUFFER_DRAW = (1 << 1),
FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW),
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
@@ -119,7 +126,7 @@ public:
struct
{
std::vector<Matrix4> transform;
std::vector<Matrix4> projection;
Matrix4 projection;
} matrices;
class TempTransform
@@ -173,7 +180,6 @@ public:
{
size_t textureMemory;
int drawCalls;
int framebufferBinds;
int shaderSwitches;
} stats;
@@ -282,7 +288,7 @@ public:
* Sets the OpenGL rendering viewport to the specified rectangle.
* The y-coordinate starts at the top.
**/
void setViewport(const Viewport &v);
void setViewport(const Viewport &v, bool canvasActive);
/**
* Gets the current OpenGL rendering viewport rectangle.
@@ -293,7 +299,7 @@ public:
* Sets the scissor box to the specified rectangle.
* The y-coordinate starts at the top and is flipped internally.
**/
void setScissor(const Viewport &v);
void setScissor(const Viewport &v, bool canvasActive);
/**
* Gets the current scissor box (regardless of whether scissoring is enabled.)
@@ -323,7 +329,9 @@ public:
/**
* Binds a Framebuffer Object to the specified target.
**/
void bindFramebuffer(GLenum target, GLuint framebuffer);
void bindFramebuffer(FramebufferTarget target, GLuint framebuffer);
GLuint getFramebuffer(FramebufferTarget target) const;
void deleteFramebuffer(GLuint framebuffer);
/**
* Calls glUseProgram.
@@ -375,6 +383,7 @@ public:
void setTextureWrap(const graphics::Texture::Wrap &w);
bool isClampZeroTextureWrapSupported() const;
bool isPixelShaderHighpSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -401,6 +410,12 @@ public:
**/
float getMaxPointSize() const;
/**
* Returns the maximum anisotropic filtering value that can be used for
* Texture filtering.
**/
float getMaxAnisotropy() const;
void updateTextureMemorySize(size_t oldsize, size_t newsize);
@@ -413,6 +428,7 @@ public:
static GLint getGLWrapMode(Texture::WrapMode wmode);
static const char *errorString(GLenum errorcode);
static const char *framebufferStatusString(GLenum status);
// Get human-readable strings for debug info.
static const char *debugSeverityString(GLenum severity);
@@ -429,6 +445,7 @@ private:
bool contextInitialized;
bool pixelShaderHighpSupported;
float maxAnisotropy;
int maxTextureSize;
int maxRenderTargets;
@@ -456,6 +473,8 @@ private:
float pointSize;
GLuint boundFramebuffers[2];
bool framebufferSRGBEnabled;
GLuint defaultTexture;
+315 -199
View File
@@ -22,11 +22,12 @@
#include "common/config.h"
#include "Shader.h"
#include "Canvas.h"
#include "Graphics.h"
// C++
#include <algorithm>
#include <limits>
#include <sstream>
namespace love
{
@@ -41,11 +42,12 @@ namespace
// reattaches the originally active program when destroyed
struct TemporaryAttacher
{
TemporaryAttacher(Shader *shader)
TemporaryAttacher(Shader *shader, bool attachNow)
: curShader(shader)
, prevShader(Shader::current)
{
curShader->attach(true);
if (attachNow)
attach();
}
~TemporaryAttacher()
@@ -56,6 +58,11 @@ namespace
curShader->detach();
}
void attach()
{
curShader->attach(true);
}
Shader *curShader;
Shader *prevShader;
};
@@ -70,14 +77,12 @@ Shader *Shader::defaultVideoShader = nullptr;
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2];
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2];
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSource &source)
: shaderSource(source)
, program(0)
, builtinUniforms()
, builtinAttributes()
, lastCanvas((Canvas *) -1)
, canvasWasActive(false)
, lastViewport()
, lastPointSize(0.0f)
, videoTextureUnits()
@@ -85,10 +90,6 @@ Shader::Shader(const ShaderSource &source)
if (source.vertex.empty() && source.pixel.empty())
throw love::Exception("Cannot create shader: no source code!");
// initialize global texture id counters if needed
if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1)
textureCounters.resize(gl.getMaxTextureUnits() - 1, 0);
// load shader source and create program object
loadVolatile();
}
@@ -98,12 +99,25 @@ Shader::~Shader()
if (current == this)
detach();
for (const auto &retainable : boundRetainables)
retainable.second->release();
boundRetainables.clear();
unloadVolatile();
for (const auto &p : uniforms)
{
// Allocated with malloc().
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
}
}
GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
@@ -177,8 +191,6 @@ void Shader::mapActiveUniforms()
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
uniforms.clear();
GLint activeprogram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
@@ -190,6 +202,9 @@ void Shader::mapActiveUniforms()
GLchar cname[256];
const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
std::map<std::string, UniformInfo> olduniforms = uniforms;
uniforms.clear();
for (int i = 0; i < numuniforms; i++)
{
GLsizei namelen = 0;
@@ -207,12 +222,6 @@ void Shader::mapActiveUniforms()
else
u.components = getUniformTypeComponents(gltype);
// Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to
// do this themselves, but some Android devices (galaxy tab 3 and 4)
// don't seem to do it...
if (u.baseType == UNIFORM_SAMPLER)
glUniform1i(u.location, 0);
// glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3)
{
@@ -226,8 +235,138 @@ void Shader::mapActiveUniforms()
if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location;
if (u.location != -1)
uniforms[u.name] = u;
if (u.location == -1)
continue;
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
auto oldu = olduniforms.find(u.name);
if (oldu != olduniforms.end())
{
u.data = oldu->second.data;
u.textures = oldu->second.textures;
updateUniform(&u, u.count, true);
if (u.baseType == UNIFORM_SAMPLER)
{
// Make sure all stored textures have their Volatiles loaded
// before the sendTextures call, since it calls getHandle().
for (int i = 0; i < u.count; i++)
{
if (u.textures[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
if (v != nullptr)
v->loadVolatile();
}
sendTextures(&u, u.textures, u.count, true);
}
}
else
{
size_t datasize = 0;
switch (u.baseType)
{
case UNIFORM_FLOAT:
datasize = sizeof(float) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
datasize = sizeof(int) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_MATRIX:
datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
u.data = malloc(datasize);
break;
default:
break;
}
if (datasize > 0)
{
memset(u.data, 0, datasize);
if (u.baseType == UNIFORM_SAMPLER)
{
// Initialize all samplers to 0. Both GLSL and GLSL ES are
// supposed to do this themselves, but some Android devices
// (galaxy tab 3 and 4) don't seem to do it...
glUniform1iv(u.location, u.count, u.ints);
u.textures = new Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
size_t offset = 0;
// Store any shader-initialized values in our own memory.
for (int i = 0; i < u.count; i++)
{
GLint location = u.location;
if (u.count > 1)
{
std::ostringstream ss;
ss << i;
std::string indexname = u.name + "[" + ss.str() + "]";
location = glGetUniformLocation(program, indexname.c_str());
}
if (location == -1)
continue;
switch (u.baseType)
{
case UNIFORM_FLOAT:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.components;
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
glGetUniformiv(program, location, &u.ints[offset]);
offset += u.components;
break;
case UNIFORM_MATRIX:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.matrix.rows * u.matrix.columns;
break;
default:
break;
}
}
}
uniforms[u.name] = u;
}
// Make sure uniforms that existed before but don't exist anymore are
// cleaned up. This theoretically shouldn't happen, but...
for (const auto &p : olduniforms)
{
if (uniforms.find(p.first) == uniforms.end())
{
free(p.second.data);
if (p.second.baseType != UNIFORM_SAMPLER)
continue;
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
}
gl.useProgram(activeprogram);
@@ -238,7 +377,7 @@ bool Shader::loadVolatile()
OpenGL::TempDebugGroup debuggroup("Shader load");
// Recreating the shader program will invalidate uniforms that rely on these.
lastCanvas = (Canvas *) -1;
canvasWasActive = false;
lastViewport = OpenGL::Viewport();
lastPointSize = -1.0f;
@@ -252,8 +391,8 @@ bool Shader::loadVolatile()
videoTextureUnits[i] = 0;
// zero out active texture list
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
textureUnits.clear();
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
std::vector<GLuint> shaderids;
@@ -340,31 +479,21 @@ bool Shader::loadVolatile()
void Shader::unloadVolatile()
{
if (current == this)
gl.useProgram(0);
if (program != 0)
{
if (current == this)
gl.useProgram(0);
glDeleteProgram(program);
program = 0;
}
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTexUnits.size(); ++i)
{
if (activeTexUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it
activeTexUnits.clear();
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0);
textureUnits.clear();
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
attributes.clear();
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
@@ -421,10 +550,10 @@ void Shader::attach(bool temporary)
{
// make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures!
for (int i = 0; i < (int) activeTexUnits.size(); ++i)
for (int i = 1; i < (int) textureUnits.size(); ++i)
{
if (activeTexUnits[i] > 0)
gl.bindTextureToUnit(activeTexUnits[i], i + 1, false);
if (textureUnits[i].active)
gl.bindTextureToUnit(textureUnits[i].texture, i, false);
}
}
}
@@ -456,160 +585,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
return &(it->second);
}
void Shader::sendInts(const UniformInfo *info, const int *vec, int count)
void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL)
return;
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, !internalUpdate);
int location = info->location;
UniformType type = info->baseType;
switch (info->components)
if (type == UNIFORM_FLOAT)
{
case 4:
glUniform4iv(location, count, vec);
break;
case 3:
glUniform3iv(location, count, vec);
break;
case 2:
glUniform2iv(location, count, vec);
break;
case 1:
default:
glUniform1iv(location, count, vec);
break;
switch (info->components)
{
case 1:
glUniform1fv(location, count, info->floats);
break;
case 2:
glUniform2fv(location, count, info->floats);
break;
case 3:
glUniform3fv(location, count, info->floats);
break;
case 4:
glUniform4fv(location, count, info->floats);
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
{
switch (info->components)
{
case 1:
glUniform1iv(location, count, info->ints);
break;
case 2:
glUniform2iv(location, count, info->ints);
break;
case 3:
glUniform3iv(location, count, info->ints);
break;
case 4:
glUniform4iv(location, count, info->ints);
break;
}
}
else if (type == UNIFORM_MATRIX)
{
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats);
}
}
void Shader::sendFloats(const UniformInfo *info, const float *vec, int count)
int Shader::getFreeTextureUnits(int count)
{
if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL)
return;
int startunit = -1;
TemporaryAttacher attacher(this);
int location = info->location;
switch (info->components)
// Ignore the first texture unit for Shader-local texture bindings.
for (int i = 1; i < (int) textureUnits.size(); i++)
{
case 4:
glUniform4fv(location, count, vec);
break;
case 3:
glUniform3fv(location, count, vec);
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 1:
default:
glUniform1fv(location, count, vec);
break;
if (!textureUnits[i].active && i + count <= (int) textureUnits.size())
{
startunit = i;
break;
}
}
if (startunit == -1)
throw love::Exception("No more texture units available for shader.");
return startunit;
}
void Shader::sendMatrices(const UniformInfo *info, const float *m, int count)
{
if (info->baseType != UNIFORM_MATRIX)
return;
TemporaryAttacher attacher(this);
int location = info->location;
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, m);
}
void Shader::sendTexture(const UniformInfo *info, Texture *texture)
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_SAMPLER)
return;
GLuint gltex = *(GLuint *) texture->getHandle();
count = std::min(count, info->count);
bool updateuniform = false;
TemporaryAttacher attacher(this);
int texunit = getTextureUnit(info->name);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(gltex, texunit, false);
glUniform1i(info->location, texunit);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTexUnits[texunit-1] == 0)
++textureCounters[texunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTexUnits[texunit-1] = gltex;
retainObject(info->name, texture);
}
void Shader::retainObject(const std::string &name, Object *object)
{
object->retain();
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
boundRetainables[name] = object;
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = texUnitPool.find(name);
if (it != texUnitPool.end())
return it->second;
int texunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
if (freeunit_it != textureCounters.end())
// Make sure the shader's samplers are associated with texture units.
for (int i = 0; i < count; i++)
{
// we don't want to use unit 0
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0);
if (info->ints[i] == 0 && textures[i] != nullptr)
{
int texunit = getFreeTextureUnits(1);
textureUnits[texunit].active = true;
if (nextunit_it == activeTexUnits.end())
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
info->ints[i] = texunit;
updateuniform = true;
}
}
texUnitPool[name] = texunit;
return texunit;
if (updateuniform)
updateUniform(info, count, internalUpdate);
// Bind the textures to the texture units.
for (int i = 0; i < count; i++)
{
if (textures[i] != nullptr)
textures[i]->retain();
if (info->textures[i] != nullptr)
info->textures[i]->release();
info->textures[i] = textures[i];
int texunit = info->ints[i];
if (textures[i] != nullptr)
{
GLuint gltex = *(GLuint *) textures[i]->getHandle();
gl.bindTextureToUnit(gltex, texunit, false);
// store texture id so it can be re-bound to the proper texture unit later
textureUnits[texunit].texture = gltex;
}
else
{
gl.bindTextureToUnit(0, texunit, false);
textureUnits[texunit].texture = 0;
textureUnits[texunit].active = false;
}
}
}
bool Shader::hasUniform(const std::string &name) const
@@ -636,12 +753,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture)
{
TemporaryAttacher attacher(this);
// Set up the texture units that will be used by the shader to sample from
// the textures, if they haven't been set up yet.
if (videoTextureUnits[0] == 0)
{
TemporaryAttacher attacher(this, true);
const GLint locs[3] = {
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
@@ -657,14 +774,15 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
{
if (locs[i] >= 0 && names[i] != nullptr)
{
videoTextureUnits[i] = getTextureUnit(names[i]);
const UniformInfo *info = getUniformInfo(names[i]);
if (info != nullptr)
{
videoTextureUnits[i] = getFreeTextureUnits(1);
textureUnits[videoTextureUnits[i]].active = true;
// Increment global shader texture id counter for this texture
// unit, if we haven't already.
if (activeTexUnits[videoTextureUnits[i] - 1] == 0)
++textureCounters[videoTextureUnits[i] - 1];
glUniform1i(locs[i], videoTextureUnits[i]);
info->ints[0] = videoTextureUnits[i];
updateUniform(info, 1);
}
}
}
}
@@ -677,7 +795,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
if (videoTextureUnits[i] != 0)
{
// Store texture id so it can be re-bound later.
activeTexUnits[videoTextureUnits[i] - 1] = textures[i];
textureUnits[videoTextureUnits[i]].texture = textures[i];
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
}
}
@@ -687,7 +805,10 @@ void Shader::checkSetScreenParams()
{
OpenGL::Viewport view = gl.getViewport();
if (view == lastViewport && lastCanvas == Canvas::current)
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
bool canvasActive = gfx->getActivePass().colorAttachmentCount > 0;
if (view == lastViewport && canvasWasActive == canvasActive)
return;
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
@@ -698,7 +819,7 @@ void Shader::checkSetScreenParams()
0.0f, 0.0f,
};
if (Canvas::current != nullptr)
if (canvasActive)
{
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
params[2] = 1.0f;
@@ -716,11 +837,11 @@ void Shader::checkSetScreenParams()
if (location >= 0)
{
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
glUniform4fv(location, 1, params);
}
lastCanvas = Canvas::current;
canvasWasActive = canvasActive;
lastViewport = view;
}
@@ -733,7 +854,7 @@ void Shader::checkSetPointSize(float size)
if (location >= 0)
{
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
glUniform1f(location, size);
}
@@ -751,9 +872,9 @@ void Shader::checkSetBuiltinUniforms()
checkSetPointSize(gl.getPointSize());
const Matrix4 &curxform = gl.matrices.transform.back();
const Matrix4 &curproj = gl.matrices.projection.back();
const Matrix4 &curproj = gl.matrices.projection;
TemporaryAttacher attacher(this);
TemporaryAttacher attacher(this, true);
bool tpmatrixneedsupdate = false;
@@ -800,11 +921,6 @@ void Shader::checkSetBuiltinUniforms()
}
}
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
{
return boundRetainables;
}
std::string Shader::getGLSLVersion()
{
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
+22 -34
View File
@@ -41,8 +41,6 @@ namespace graphics
namespace opengl
{
class Canvas;
// A GLSL shader
class Shader : public Object, public Volatile
{
@@ -100,13 +98,24 @@ public:
{
int location;
int count;
union
{
int components;
MatrixSize matrix;
};
UniformType baseType;
std::string name;
union
{
void *data;
float *floats;
int *ints;
};
Texture **textures;
};
// Pointer to currently active Shader.
@@ -151,11 +160,9 @@ public:
std::string getWarnings() const;
const UniformInfo *getUniformInfo(const std::string &name) const;
void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false);
void sendInts(const UniformInfo *info, const int *vec, int count);
void sendFloats(const UniformInfo *info, const float *vec, int count);
void sendMatrices(const UniformInfo *info, const float *m, int count);
void sendTexture(const UniformInfo *info, Texture *texture);
void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false);
/**
* Gets whether a uniform with the specified name exists and is actively
@@ -175,24 +182,11 @@ public:
void checkSetPointSize(float size);
void checkSetBuiltinUniforms();
const std::map<std::string, Object *> &getBoundRetainables() const;
GLuint getProgram() const
{
return program;
}
template <typename T>
T *getScratchBuffer(size_t count)
{
size_t bytes = sizeof(T) * count;
if (scratchBuffer.size() < bytes)
scratchBuffer.resize(bytes);
return (T *) scratchBuffer.data();
}
static std::string getGLSLVersion();
static bool isSupported();
@@ -204,6 +198,12 @@ public:
private:
struct TextureUnit
{
GLuint texture = 0;
bool active = false;
};
// Map active uniform names to their locations.
void mapActiveUniforms();
@@ -213,9 +213,7 @@ private:
GLuint compileCode(ShaderStage stage, const std::string &code);
int getTextureUnit(const std::string &name);
void retainObject(const std::string &name, Object *object);
int getFreeTextureUnits(int count);
// Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const;
@@ -241,14 +239,9 @@ private:
std::map<std::string, UniformInfo> uniforms;
// Texture unit pool for setting images
std::map<std::string, GLint> texUnitPool; // texUnitPool[name] = textureunit
std::vector<GLuint> activeTexUnits; // activeTexUnits[textureunit-1] = textureid
std::vector<TextureUnit> textureUnits;
// Uniform name to retainable objects
std::map<std::string, Object*> boundRetainables;
// Pointer to the active Canvas when the screen params were last checked.
Canvas *lastCanvas;
bool canvasWasActive;
OpenGL::Viewport lastViewport;
float lastPointSize;
@@ -258,11 +251,6 @@ private:
GLuint videoTextureUnits[3];
std::vector<char> scratchBuffer;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames;
@@ -33,55 +33,6 @@ Canvas *luax_checkcanvas(lua_State *L, int idx)
return luax_checktype<Canvas>(L, idx);
}
int w_Canvas_renderTo(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (graphics)
{
// Save the current Canvas so we can restore it when we're done.
std::vector<Canvas *> oldcanvases = graphics->getCanvas();
for (Canvas *c : oldcanvases)
c->retain();
luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); });
lua_settop(L, 2); // make sure the function is on top of the stack
int status = lua_pcall(L, 0, 0, 0);
graphics->setCanvas(oldcanvases);
for (Canvas *c : oldcanvases)
c->release();
if (status != 0)
return lua_error(L);
}
return 0;
}
int w_Canvas_newImageData(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
love::image::Image *image = luax_getmodule<love::image::Image>(L);
int x = (int) luaL_optnumber(L, 2, 0);
int y = (int) luaL_optnumber(L, 3, 0);
int w = (int) luaL_optnumber(L, 4, canvas->getWidth());
int h = (int) luaL_optnumber(L, 5, canvas->getHeight());
love::image::ImageData *img = nullptr;
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, x, y, w, h); });
luax_pushtype(L, img);
img->release();
return 1;
}
int w_Canvas_getFormat(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
@@ -103,8 +54,6 @@ int w_Canvas_getMSAA(lua_State *L)
static const luaL_Reg w_Canvas_functions[] =
{
{ "renderTo", w_Canvas_renderTo },
{ "newImageData", w_Canvas_newImageData },
{ "getFormat", w_Canvas_getFormat },
{ "getMSAA", w_Canvas_getMSAA },
{ 0, 0 }
+410 -223
View File
@@ -27,6 +27,8 @@
#include "filesystem/wrap_Filesystem.h"
#include "video/VideoStream.h"
#include "image/wrap_Image.h"
#include "common/Reference.h"
#include "math/wrap_Transform.h"
#include <cassert>
#include <cstring>
@@ -60,79 +62,9 @@ int w_reset(lua_State *)
return 0;
}
int w_clear(lua_State *L)
int w_present(lua_State *L)
{
Colorf color;
if (lua_isnoneornil(L, 1))
color.set(0.0, 0.0, 0.0, 0.0);
else if (lua_istable(L, 1))
{
std::vector<Graphics::OptionalColorf> colors((size_t) lua_gettop(L));
for (int i = 0; i < lua_gettop(L); i++)
{
if (lua_isnoneornil(L, i + 1) || luax_objlen(L, i + 1) == 0)
{
colors[i].enabled = false;
continue;
}
for (int j = 1; j <= 4; j++)
lua_rawgeti(L, i + 1, j);
colors[i].enabled = true;
colors[i].r = (float) luaL_checknumber(L, -4);
colors[i].g = (float) luaL_checknumber(L, -3);
colors[i].b = (float) luaL_checknumber(L, -2);
colors[i].a = (float) luaL_optnumber(L, -1, 1.0);
lua_pop(L, 4);
}
luax_catchexcept(L, [&]() { instance()->clear(colors); });
return 0;
}
else
{
color.r = (float) luaL_checknumber(L, 1);
color.g = (float) luaL_checknumber(L, 2);
color.b = (float) luaL_checknumber(L, 3);
color.a = (float) luaL_optnumber(L, 4, 1.0);
}
luax_catchexcept(L, [&]() { instance()->clear(color); });
return 0;
}
int w_discard(lua_State *L)
{
std::vector<bool> colorbuffers;
if (lua_istable(L, 1))
{
for (size_t i = 1; i <= luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
colorbuffers.push_back(luax_optboolean(L, -1, true));
lua_pop(L, 1);
}
}
else
{
bool discardcolor = luax_optboolean(L, 1, true);
size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().size());
colorbuffers = std::vector<bool>(numbuffers, discardcolor);
}
bool stencil = luax_optboolean(L, 2, true);
instance()->discard(colorbuffers, stencil);
return 0;
}
int w_present(lua_State *)
{
instance()->present();
luax_catchexcept(L, [&]() { instance()->present(L); });
return 0;
}
@@ -173,6 +105,274 @@ int w_getDimensions(lua_State *L)
return 2;
}
int w_getPassWidth(lua_State *L)
{
lua_pushinteger(L, instance()->getPassWidth());
return 1;
}
int w_getPassHeight(lua_State *L)
{
lua_pushinteger(L, instance()->getPassHeight());
return 1;
}
int w_getPassDimensions(lua_State *L)
{
lua_pushinteger(L, instance()->getPassWidth());
lua_pushinteger(L, instance()->getPassHeight());
return 2;
}
static int w__beginPass(lua_State *L)
{
int nextstartidx = 1;
if (lua_isnoneornil(L, 1))
{
luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_LOAD, Colorf()); });
nextstartidx = 1;
}
else if (lua_isnumber(L, 1))
{
Colorf c;
c.r = (float) luaL_checknumber(L, 1);
c.g = (float) luaL_checknumber(L, 2);
c.b = (float) luaL_checknumber(L, 3);
if (lua_isnumber(L, 4))
{
c.a = (float) lua_tonumber(L, 4);
nextstartidx = 5;
}
else
{
c.a = 1.0f;
nextstartidx = 4;
}
luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_CLEAR, c); });
}
else if (luax_istype(L, 1, Canvas::type))
{
PassInfo::ColorAttachment attachment;
attachment.canvas = luax_checkcanvas(L, 1);
attachment.beginAction = PassInfo::BEGIN_LOAD;
if (lua_isnumber(L, 2))
{
attachment.beginAction = PassInfo::BEGIN_CLEAR;
attachment.clearColor.r = (float) luaL_checknumber(L, 2);
attachment.clearColor.g = (float) luaL_checknumber(L, 3);
attachment.clearColor.b = (float) luaL_checknumber(L, 4);
if (lua_isnumber(L, 5))
{
attachment.clearColor.a = (float) lua_tonumber(L, 5);
nextstartidx = 6;
}
else
{
attachment.clearColor.a = 1.0f;
nextstartidx = 5;
}
}
else
nextstartidx = 2;
PassInfo info;
info.addColorAttachment(attachment);
if (lua_isboolean(L, nextstartidx))
{
info.stencil = luax_toboolean(L, nextstartidx);
nextstartidx++;
}
else
info.stencil = false;
luax_catchexcept(L, [&]() { instance()->beginPass(info); });
}
else
{
luaL_checktype(L, 1, LUA_TTABLE);
int nattachments = std::max((int) luax_objlen(L, 1), 1);
if (nattachments > MAX_COLOR_RENDER_TARGETS)
return luaL_error(L, "Cannot render to %d Canvases at once!", nattachments);
PassInfo info;
for (int i = 1; i <= nattachments; i++)
{
lua_rawgeti(L, 1, i);
luaL_checktype(L, -1, LUA_TTABLE);
PassInfo::ColorAttachment attachment;
attachment.beginAction = PassInfo::BEGIN_LOAD;
lua_rawgeti(L, -1, 1);
attachment.canvas = luax_checkcanvas(L, -1);
lua_rawgeti(L, -2, 2);
if (!lua_isnoneornil(L, -1))
{
attachment.beginAction = PassInfo::BEGIN_CLEAR;
for (int j = 3; j < 6; j++)
lua_rawgeti(L, -j, j);
attachment.clearColor.r = (float) luaL_checknumber(L, -4);
attachment.clearColor.g = (float) luaL_checknumber(L, -3);
attachment.clearColor.b = (float) luaL_checknumber(L, -2);
attachment.clearColor.a = (float) luaL_optnumber(L, -1, 1.0);
}
lua_pop(L, 2 + (attachment.beginAction == PassInfo::BEGIN_CLEAR ? 4 : 1));
info.addColorAttachment(attachment);
}
info.stencil = luax_boolflag(L, 1, "stencil", false);
luax_catchexcept(L, [&]() { instance()->beginPass(info); });
nextstartidx = 2;
}
return nextstartidx;
}
int w_beginPass(lua_State *L)
{
w__beginPass(L);
return 0;
}
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
{
if (i != nullptr)
{
lua_State *L = (lua_State *) gd;
ref->push(L);
delete ref;
luax_pushtype(L, i);
lua_call(L, 1, 0);
}
else
delete ref;
}
static int w__endPass(lua_State *L, int startidx)
{
if (lua_isnoneornil(L, startidx))
{
luax_catchexcept(L, []() { instance()->endPass(); });
}
else
{
int x, y, w, h;
if (lua_isnumber(L, startidx))
{
x = (int) luaL_checknumber(L, 1);
y = (int) luaL_checknumber(L, 2);
w = (int) luaL_checknumber(L, 3);
h = (int) luaL_checknumber(L, 4);
startidx += 4;
}
else
{
x = 0;
y = 0;
w = instance()->getPassWidth();
h = instance()->getPassHeight();
}
luaL_checktype(L, startidx, LUA_TFUNCTION);
Graphics::ScreenshotInfo info;
info.callback = screenshotCallback;
lua_pushvalue(L, startidx);
info.ref = luax_refif(L, LUA_TFUNCTION);
lua_pop(L, 1);
luax_catchexcept(L,
[&]() { instance()->endPass(x, y, w, h, &info, L); },
[&](bool except) { if (except) delete info.ref; }
);
}
return 0;
}
int w_endPass(lua_State *L)
{
return w__endPass(L, 1);
}
int w_renderPass(lua_State *L)
{
int startidx = w__beginPass(L);
if (lua_type(L, startidx) != LUA_TFUNCTION)
{
w__endPass(L, startidx + 1);
luaL_checktype(L, startidx, LUA_TFUNCTION);
return 0;
}
int nargs = lua_gettop(L) - startidx;
int status = lua_pcall(L, nargs, 0, 0);
w__endPass(L, startidx + 1);
if (status != 0)
return lua_error(L);
return 0;
}
int w_isPassActive(lua_State *L)
{
luax_pushboolean(L, instance()->isPassActive());
return 1;
}
int w_getPassCanvases(lua_State *L)
{
if (!instance()->isPassActive())
return 0;
const PassInfo &info = instance()->getActivePass();
for (const auto &attachment : info.colorAttachments)
luax_pushtype(L, attachment.canvas);
return info.colorAttachmentCount;
}
int w_captureScreenshot(lua_State *L)
{
luaL_checktype(L, 1, LUA_TFUNCTION);
Graphics::ScreenshotInfo info;
info.callback = screenshotCallback;
lua_pushvalue(L, 1);
info.ref = luax_refif(L, LUA_TFUNCTION);
lua_pop(L, 1);
luax_catchexcept(L,
[&]() { instance()->captureScreenshot(info); },
[&](bool except) { if (except) delete info.ref; }
);
return 0;
}
int w_setScissor(lua_State *L)
{
int nargs = lua_gettop(L);
@@ -243,13 +443,13 @@ int w_stencil(lua_State *L)
if (lua_toboolean(L, 4) == 0)
instance()->clearStencil();
instance()->drawToStencilBuffer(action, stencilvalue);
luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); });
// Call stencilfunc()
lua_pushvalue(L, 1);
lua_call(L, 0, 0);
instance()->stopDrawToStencilBuffer();
luax_catchexcept(L, [&](){ instance()->stopDrawToStencilBuffer(); });
return 0;
}
@@ -268,7 +468,7 @@ int w_setStencilTest(lua_State *L)
comparevalue = (int) luaL_checknumber(L, 2);
}
instance()->setStencilTest(compare, comparevalue);
luax_catchexcept(L, [&](){ instance()->setStencilTest(compare, comparevalue); });
return 0;
}
@@ -1246,79 +1446,6 @@ int w_isWireframe(lua_State *L)
return 1;
}
int w_newScreenshot(lua_State *L)
{
love::image::Image *image = luax_getmodule<love::image::Image>(L);
bool copyAlpha = luax_optboolean(L, 1, false);
love::image::ImageData *i = 0;
luax_catchexcept(L, [&](){ i = instance()->newScreenshot(image, copyAlpha); });
luax_pushtype(L, i);
i->release();
return 1;
}
int w_setCanvas(lua_State *L)
{
// Disable stencil writes.
instance()->stopDrawToStencilBuffer();
// called with none -> reset to default buffer
if (lua_isnoneornil(L, 1))
{
instance()->setCanvas();
return 0;
}
bool is_table = lua_istable(L, 1);
std::vector<Canvas *> canvases;
if (is_table)
{
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
canvases.push_back(luax_checkcanvas(L, -1));
lua_pop(L, 1);
}
}
else
{
for (int i = 1; i <= lua_gettop(L); i++)
canvases.push_back(luax_checkcanvas(L, i));
}
luax_catchexcept(L, [&]() {
if (canvases.size() > 0)
instance()->setCanvas(canvases);
else
instance()->setCanvas();
});
return 0;
}
int w_getCanvas(lua_State *L)
{
const std::vector<Canvas *> canvases = instance()->getCanvas();
int n = 0;
for (Canvas *c : canvases)
{
luax_pushtype(L, c);
n++;
}
if (n == 0)
{
lua_pushnil(L);
n = 1;
}
return n;
}
int w_setShader(lua_State *L)
{
if (lua_isnoneornil(L,1))
@@ -1497,8 +1624,8 @@ int w_getStats(lua_State *L)
lua_pushinteger(L, stats.drawCalls);
lua_setfield(L, -2, "drawcalls");
lua_pushinteger(L, stats.canvasSwitches);
lua_setfield(L, -2, "canvasswitches");
lua_pushinteger(L, stats.renderPasses);
lua_setfield(L, -2, "renderpasses");
lua_pushinteger(L, stats.shaderSwitches);
lua_setfield(L, -2, "shaderswitches");
@@ -1541,24 +1668,37 @@ int w_draw(lua_State *L)
startidx = 2;
}
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
if (luax_istype(L, startidx, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, startidx);
luax_catchexcept(L, [&]() {
if (texture && quad)
instance()->drawq(texture, quad, tf->getMatrix());
else
instance()->draw(drawable, tf->getMatrix());
});
}
else
{
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&]() {
if (texture && quad)
texture->drawq(quad, m);
else if (drawable)
drawable->draw(m);
});
luax_catchexcept(L, [&]() {
if (texture && quad)
instance()->drawq(texture, quad, m);
else if (drawable)
instance()->draw(drawable, m);
});
}
return 0;
}
@@ -1568,19 +1708,27 @@ int w_print(lua_State *L)
std::vector<Font::ColoredString> str;
luax_checkcoloredstring(L, 1, str);
float x = (float)luaL_optnumber(L, 2, 0.0);
float y = (float)luaL_optnumber(L, 3, 0.0);
float angle = (float)luaL_optnumber(L, 4, 0.0f);
float sx = (float)luaL_optnumber(L, 5, 1.0f);
float sy = (float)luaL_optnumber(L, 6, sx);
float ox = (float)luaL_optnumber(L, 7, 0.0f);
float oy = (float)luaL_optnumber(L, 8, 0.0f);
float kx = (float)luaL_optnumber(L, 9, 0.0f);
float ky = (float)luaL_optnumber(L, 10, 0.0f);
if (luax_istype(L, 2, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 2);
luax_catchexcept(L, [&](){ instance()->print(str, tf->getMatrix()); });
}
else
{
float x = (float)luaL_optnumber(L, 2, 0.0);
float y = (float)luaL_optnumber(L, 3, 0.0);
float angle = (float)luaL_optnumber(L, 4, 0.0f);
float sx = (float)luaL_optnumber(L, 5, 1.0f);
float sy = (float)luaL_optnumber(L, 6, sx);
float ox = (float)luaL_optnumber(L, 7, 0.0f);
float oy = (float)luaL_optnumber(L, 8, 0.0f);
float kx = (float)luaL_optnumber(L, 9, 0.0f);
float ky = (float)luaL_optnumber(L, 10, 0.0f);
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ instance()->print(str, m); });
luax_catchexcept(L, [&](){ instance()->print(str, m); });
}
return 0;
}
@@ -1589,36 +1737,38 @@ int w_printf(lua_State *L)
std::vector<Font::ColoredString> str;
luax_checkcoloredstring(L, 1, str);
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
float wrap = (float)luaL_checknumber(L, 4);
float angle = 0.0f;
float sx = 1.0f, sy = 1.0f;
float ox = 0.0f, oy = 0.0f;
float kx = 0.0f, ky = 0.0f;
Font::AlignMode align = Font::ALIGN_LEFT;
Matrix4 m;
if (lua_gettop(L) >= 5)
int formatidx = 4;
if (luax_istype(L, 2, math::Transform::type))
{
if (!lua_isnil(L, 5))
{
const char *str = luaL_checkstring(L, 5);
if (!Font::getConstant(str, align))
return luaL_error(L, "Incorrect alignment: %s", str);
}
math::Transform *tf = luax_totype<math::Transform>(L, 2);
m = tf->getMatrix();
formatidx = 3;
}
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
angle = (float) luaL_optnumber(L, 6, 0.0f);
sx = (float) luaL_optnumber(L, 7, 1.0f);
sy = (float) luaL_optnumber(L, 8, sx);
ox = (float) luaL_optnumber(L, 9, 0.0f);
oy = (float) luaL_optnumber(L, 10, 0.0f);
kx = (float) luaL_optnumber(L, 11, 0.0f);
ky = (float) luaL_optnumber(L, 12, 0.0f);
float angle = (float) luaL_optnumber(L, 6, 0.0f);
float sx = (float) luaL_optnumber(L, 7, 1.0f);
float sy = (float) luaL_optnumber(L, 8, sx);
float ox = (float) luaL_optnumber(L, 9, 0.0f);
float oy = (float) luaL_optnumber(L, 10, 0.0f);
float kx = (float) luaL_optnumber(L, 11, 0.0f);
float ky = (float) luaL_optnumber(L, 12, 0.0f);
m = Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
}
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
float wrap = (float)luaL_checknumber(L, formatidx);
const char *astr = lua_isnoneornil(L, formatidx + 1) ? nullptr : luaL_checkstring(L, formatidx + 1);
if (astr != nullptr && !Font::getConstant(astr, align))
return luaL_error(L, "Incorrect alignment: %s", astr);
luax_catchexcept(L, [&](){ instance()->printf(str, wrap, align, m); });
return 0;
@@ -1698,11 +1848,14 @@ int w_points(lua_State *L)
coords[i] = luax_tofloat(L, i + 1);
}
instance()->points(coords, colors, numpoints);
delete[] coords;
if (colors)
delete[] colors;
luax_catchexcept(L,
[&](){ instance()->points(coords, colors, numpoints); },
[&](bool) {
delete[] coords;
if (colors)
delete[] colors;
}
);
return 0;
}
@@ -1738,9 +1891,11 @@ int w_line(lua_State *L)
coords[i] = luax_tofloat(L, i + 1);
}
instance()->polyline(coords, args);
luax_catchexcept(L,
[&](){ instance()->polyline(coords, args); },
[&](bool) { delete[] coords; }
);
delete[] coords;
return 0;
}
@@ -1766,11 +1921,11 @@ int w_rectangle(lua_State *L)
float ry = (float)luaL_optnumber(L, 7, rx);
if (lua_isnoneornil(L, 8))
instance()->rectangle(mode, x, y, w, h, rx, ry);
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry); });
else
{
int points = (int) luaL_checknumber(L, 8);
instance()->rectangle(mode, x, y, w, h, rx, ry, points);
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry, points); });
}
return 0;
@@ -1788,11 +1943,11 @@ int w_circle(lua_State *L)
float radius = (float)luaL_checknumber(L, 4);
if (lua_isnoneornil(L, 5))
instance()->circle(mode, x, y, radius);
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius); });
else
{
int points = (int) luaL_checknumber(L, 5);
instance()->circle(mode, x, y, radius, points);
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius, points); });
}
return 0;
@@ -1811,11 +1966,11 @@ int w_ellipse(lua_State *L)
float b = (float)luaL_optnumber(L, 5, a);
if (lua_isnoneornil(L, 6))
instance()->ellipse(mode, x, y, a, b);
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b); });
else
{
int points = (int) luaL_checknumber(L, 6);
instance()->ellipse(mode, x, y, a, b, points);
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b, points); });
}
return 0;
@@ -1848,11 +2003,11 @@ int w_arc(lua_State *L)
float angle2 = (float) luaL_checknumber(L, startidx + 4);
if (lua_isnoneornil(L, startidx + 5))
instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2);
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); });
else
{
int points = (int) luaL_checknumber(L, startidx + 5);
instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points);
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); });
}
return 0;
@@ -1900,8 +2055,11 @@ int w_polygon(lua_State *L)
// make a closed loop
coords[args] = coords[0];
coords[args+1] = coords[1];
instance()->polygon(mode, coords, args+2);
delete[] coords;
luax_catchexcept(L,
[&](){ instance()->polygon(mode, coords, args+2); },
[&](bool) { delete[] coords; }
);
return 0;
}
@@ -1914,6 +2072,13 @@ int w_push(lua_State *L)
return luaL_error(L, "Invalid graphics stack type: %s", sname);
luax_catchexcept(L, [&](){ instance()->push(stype); });
if (luax_istype(L, 2, math::Transform::type))
{
math::Transform *t = luax_totype<math::Transform>(L, 2);
instance()->applyTransform(t);
}
return 0;
}
@@ -1960,6 +2125,20 @@ int w_origin(lua_State * /*L*/)
return 0;
}
int w_applyTransform(lua_State *L)
{
math::Transform *t = math::luax_checktransform(L, 1);
instance()->applyTransform(t);
return 0;
}
int w_replaceTransform(lua_State *L)
{
math::Transform *t = math::luax_checktransform(L, 1);
instance()->replaceTransform(t);
return 0;
}
int w_transformPoint(lua_State *L)
{
Vector p;
@@ -1987,8 +2166,6 @@ int w_inverseTransformPoint(lua_State *L)
static const luaL_Reg functions[] =
{
{ "reset", w_reset },
{ "clear", w_clear },
{ "discard", w_discard },
{ "present", w_present },
{ "newImage", w_newImage },
@@ -2030,9 +2207,6 @@ static const luaL_Reg functions[] =
{ "getPointSize", w_getPointSize },
{ "setWireframe", w_setWireframe },
{ "isWireframe", w_isWireframe },
{ "newScreenshot", w_newScreenshot },
{ "setCanvas", w_setCanvas },
{ "getCanvas", w_getCanvas },
{ "setShader", w_setShader },
{ "getShader", w_getShader },
@@ -2046,6 +2220,14 @@ static const luaL_Reg functions[] =
{ "getSystemLimits", w_getSystemLimits },
{ "getStats", w_getStats },
{ "beginPass", w_beginPass },
{ "endPass", w_endPass },
{ "renderPass", w_renderPass },
{ "isPassActive", w_isPassActive },
{ "getPassCanvases", w_getPassCanvases },
{ "captureScreenshot", w_captureScreenshot },
{ "draw", w_draw },
{ "print", w_print },
@@ -2057,6 +2239,9 @@ static const luaL_Reg functions[] =
{ "getWidth", w_getWidth },
{ "getHeight", w_getHeight },
{ "getDimensions", w_getDimensions },
{ "getPassWidth", w_getPassWidth },
{ "getPassHeight", w_getPassHeight },
{ "getPassDimensions", w_getPassDimensions },
{ "setScissor", w_setScissor },
{ "intersectScissor", w_intersectScissor },
@@ -2082,6 +2267,8 @@ static const luaL_Reg functions[] =
{ "translate", w_translate },
{ "shear", w_shear },
{ "origin", w_origin },
{ "applyTransform", w_applyTransform },
{ "replaceTransform", w_replaceTransform },
{ "transformPoint", w_transformPoint },
{ "inverseTransformPoint", w_inverseTransformPoint },
+13 -1
View File
@@ -338,10 +338,21 @@ int w_Mesh_attachAttribute(lua_State *L)
Mesh *t = luax_checkmesh(L, 1);
const char *name = luaL_checkstring(L, 2);
Mesh *mesh = luax_checkmesh(L, 3);
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh); });
const char *attachname = luaL_optstring(L, 4, name);
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname); });
return 0;
}
int w_Mesh_detachAttribute(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const char *name = luaL_checkstring(L, 2);
bool success = false;
luax_catchexcept(L, [&](){ success = t->detachAttribute(name); });
luax_pushboolean(L, success);
return 1;
}
int w_Mesh_flush(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -514,6 +525,7 @@ static const luaL_Reg w_Mesh_functions[] =
{ "setAttributeEnabled", w_Mesh_setAttributeEnabled },
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
{ "attachAttribute", w_Mesh_attachAttribute },
{ "detachAttribute", w_Mesh_detachAttribute },
{ "flush", w_Mesh_flush },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
+33 -22
View File
@@ -21,6 +21,7 @@
#include "wrap_Shader.h"
#include "graphics/wrap_Texture.h"
#include "math/MathModule.h"
#include "math/Transform.h"
#include <string>
#include <algorithm>
@@ -48,14 +49,12 @@ int w_Shader_getWarnings(lua_State *L)
static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info)
{
return std::min(std::max(lua_gettop(L) - startidx, 1), info->count);
return std::min(std::max(lua_gettop(L) - startidx + 1, 1), info->count);
}
template <typename T>
static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count)
static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count)
{
T *values = shader->getScratchBuffer<T>(components * count);
if (components == 1)
{
for (int i = 0; i < count; ++i)
@@ -76,16 +75,15 @@ static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components
lua_pop(L, components);
}
}
return values;
}
int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
{
int count = _getCount(L, startidx, info);
int components = info->components;
float *values = info->floats;
float *values = _getNumbers<float>(L, startidx, shader, components, count);
_updateNumbers(L, startidx, values, components, count);
if (colors && graphics::isGammaCorrect())
{
@@ -99,15 +97,15 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader
}
}
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); });
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{
int count = _getCount(L, startidx, info);
int *values = _getNumbers<int>(L, startidx, shader, info->components, count);
luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); });
_updateNumbers(L, startidx, info->ints, info->components, count);
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
@@ -116,15 +114,15 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
int count = _getCount(L, startidx, info);
int components = info->components;
// We have to send booleans as ints or floats.
float *values = shader->getScratchBuffer<float>(components * count);
// We have to send booleans as ints.
int *values = info->ints;
if (components == 1)
{
for (int i = 0; i < count; i++)
{
luaL_checktype(L, startidx + i, LUA_TBOOLEAN);
values[i] = (float) lua_toboolean(L, startidx + i);
values[i] = (int) lua_toboolean(L, startidx + i);
}
}
else
@@ -137,14 +135,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
{
lua_rawgeti(L, startidx + i, k);
luaL_checktype(L, -1, LUA_TBOOLEAN);
values[i * components + k - 1] = (float) lua_toboolean(L, -1);
values[i * components + k - 1] = (int) lua_toboolean(L, -1);
}
lua_pop(L, components);
}
}
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); });
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0;
}
@@ -163,10 +161,17 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
int rows = info->matrix.rows;
int elements = columns * rows;
float *values = shader->getScratchBuffer<float>(elements * count);
float *values = info->floats;
for (int i = 0; i < count; i++)
{
if (columns == 4 && rows == 4 && luax_istype(L, startidx + i, math::Transform::type))
{
math::Transform *t = luax_totype<math::Transform>(L, startidx + i);
memcpy(&values[i * 16], t->getMatrix().getElements(), sizeof(float) * 16);
continue;
}
luaL_checktype(L, startidx + i, LUA_TTABLE);
lua_rawgeti(L, startidx + i, 1);
@@ -243,15 +248,21 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
}
}
shader->sendMatrices(info, values, count);
shader->updateUniform(info, count);
return 0;
}
int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{
// We don't support arrays of textures (yet).
Texture *texture = luax_checktexture(L, startidx);
luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); });
int count = _getCount(L, startidx, info);
std::vector<Texture *> textures;
textures.reserve(count);
for (int i = 0; i < count; i++)
textures.push_back(luax_checktexture(L, startidx + i));
luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); });
return 0;
}
@@ -277,7 +288,7 @@ int w_Shader_send(lua_State *L)
case Shader::UNIFORM_BOOL:
return w_Shader_sendBooleans(L, startidx, shader, info);
case Shader::UNIFORM_SAMPLER:
return w_Shader_sendTexture(L, startidx, shader, info);
return w_Shader_sendTextures(L, startidx, shader, info);
default:
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
}
@@ -23,6 +23,7 @@
#include "Image.h"
#include "Canvas.h"
#include "graphics/wrap_Texture.h"
#include "math/wrap_Transform.h"
// C++
#include <typeinfo>
@@ -51,24 +52,37 @@ static inline int w_SpriteBatch_add_or_set(lua_State *L, SpriteBatch *t, int sta
else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1))
return luax_typerror(L, startidx, "Quad");
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
if (luax_istype(L, startidx, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, startidx);
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, tf->getMatrix(), index);
else
index = t->add(tf->getMatrix(), index);
});
}
else
{
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, m, index);
else
index = t->add(m, index);
});
luax_catchexcept(L, [&]() {
if (quad)
index = t->addq(quad, m, index);
else
index = t->add(m, index);
});
}
return index;
}
+45 -26
View File
@@ -19,6 +19,7 @@
**/
#include "wrap_Text.h"
#include "math/wrap_Transform.h"
namespace love
{
@@ -126,24 +127,33 @@ int w_Text_add(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = 0;
std::vector<Font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
float x = (float) luaL_optnumber(L, 3, 0.0);
float y = (float) luaL_optnumber(L, 4, 0.0);
float a = (float) luaL_optnumber(L, 5, 0.0);
float sx = (float) luaL_optnumber(L, 6, 1.0);
float sy = (float) luaL_optnumber(L, 7, sx);
float ox = (float) luaL_optnumber(L, 8, 0.0);
float oy = (float) luaL_optnumber(L, 9, 0.0);
float kx = (float) luaL_optnumber(L, 10, 0.0);
float ky = (float) luaL_optnumber(L, 11, 0.0);
if (luax_istype(L, 3, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 3);
luax_catchexcept(L, [&](){ index = t->add(text, tf->getMatrix()); });
}
else
{
float x = (float) luaL_optnumber(L, 3, 0.0);
float y = (float) luaL_optnumber(L, 4, 0.0);
float a = (float) luaL_optnumber(L, 5, 0.0);
float sx = (float) luaL_optnumber(L, 6, 1.0);
float sy = (float) luaL_optnumber(L, 7, sx);
float ox = (float) luaL_optnumber(L, 8, 0.0);
float oy = (float) luaL_optnumber(L, 9, 0.0);
float kx = (float) luaL_optnumber(L, 10, 0.0);
float ky = (float) luaL_optnumber(L, 11, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ index = t->add(text, m); });
}
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
int index = 0;
luax_catchexcept(L, [&](){ index = t->add(text, m); });
lua_pushnumber(L, index + 1);
return 1;
}
@@ -151,6 +161,8 @@ int w_Text_addf(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = 0;
std::vector<Font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
@@ -162,21 +174,28 @@ int w_Text_addf(lua_State *L)
if (!Font::getConstant(alignstr, align))
return luaL_error(L, "Invalid align mode: %s", alignstr);
float x = (float) luaL_optnumber(L, 5, 0.0);
float y = (float) luaL_optnumber(L, 6, 0.0);
float a = (float) luaL_optnumber(L, 7, 0.0);
float sx = (float) luaL_optnumber(L, 8, 1.0);
float sy = (float) luaL_optnumber(L, 9, sx);
float ox = (float) luaL_optnumber(L, 10, 0.0);
float oy = (float) luaL_optnumber(L, 11, 0.0);
float kx = (float) luaL_optnumber(L, 12, 0.0);
float ky = (float) luaL_optnumber(L, 13, 0.0);
if (luax_istype(L, 5, math::Transform::type))
{
math::Transform *tf = luax_totype<math::Transform>(L, 5);
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, tf->getMatrix()); });
}
else
{
float x = (float) luaL_optnumber(L, 5, 0.0);
float y = (float) luaL_optnumber(L, 6, 0.0);
float a = (float) luaL_optnumber(L, 7, 0.0);
float sx = (float) luaL_optnumber(L, 8, 1.0);
float sy = (float) luaL_optnumber(L, 9, sx);
float ox = (float) luaL_optnumber(L, 10, 0.0);
float oy = (float) luaL_optnumber(L, 11, 0.0);
float kx = (float) luaL_optnumber(L, 12, 0.0);
float ky = (float) luaL_optnumber(L, 13, 0.0);
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); });
}
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
int index = 0;
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); });
lua_pushnumber(L, index + 1);
return 1;
}