mirror of
https://github.com/love2d/love.git
synced 2026-08-19 04:06:17 +02:00
Revert most render pass API changes. The APIs need more work and iteration before they’re better to use than the old setCanvas APIs.
love.graphics.captureScreenshot has not been reverted. --HG-- branch : minor
This commit is contained in:
+3
-1
@@ -16,6 +16,7 @@ Released: N/A
|
|||||||
* Added Shader:hasUniform.
|
* Added Shader:hasUniform.
|
||||||
* Added support for non-square shader uniform matrices on desktop platforms.
|
* Added support for non-square shader uniform matrices on desktop platforms.
|
||||||
* Added Shader:send(matrixname, is_column_major, matrix, ...) which specifies how to interpret the matrix table arguments.
|
* Added Shader:send(matrixname, is_column_major, matrix, ...) which specifies how to interpret the matrix table arguments.
|
||||||
|
* Added love.graphics.captureScreenshot (replaces love.graphics.newScreenshot).
|
||||||
* Added love.window.updateMode.
|
* Added love.window.updateMode.
|
||||||
* Added Object:release.
|
* Added Object:release.
|
||||||
* Added queueable audio sources.
|
* Added queueable audio sources.
|
||||||
@@ -30,15 +31,16 @@ Released: N/A
|
|||||||
* Changed all color values to be in the range 0-1, rather than 0-255.
|
* Changed all color values to be in the range 0-1, rather than 0-255.
|
||||||
* Changed love.graphics.print and friends to ignore carriage returns.
|
* Changed love.graphics.print and friends to ignore carriage returns.
|
||||||
* Changed the 'multiply' blend mode to error if not used with the 'premultiplied' blend alpha mode, since the formula only works with that anyway.
|
* Changed the 'multiply' blend mode to error if not used with the 'premultiplied' blend alpha mode, since the formula only works with that anyway.
|
||||||
|
* Changed some love.graphics, love.window, and love.event APIs to cause an error if a Canvas is active.
|
||||||
* Changed the audio playback APIs drastically.
|
* Changed the audio playback APIs drastically.
|
||||||
* Changed enet to no longer set the 'enet' global, again matching luasocket.
|
* Changed enet to no longer set the 'enet' global, again matching luasocket.
|
||||||
* Changed Source seeking behaviour, all kinds of Sources now behave similarly when seeking past the boundaries.
|
* Changed Source seeking behaviour, all kinds of Sources now behave similarly when seeking past the boundaries.
|
||||||
* Changed the rendering API to one based on render passes.
|
|
||||||
|
|
||||||
* Removed the default source type for love.audio.newSource.
|
* Removed the default source type for love.audio.newSource.
|
||||||
* Removed variant of love.filesystem.newFileData which takes base64 data, use love.math.decode instead.
|
* Removed variant of love.filesystem.newFileData which takes base64 data, use love.math.decode instead.
|
||||||
* Removed the no-argument variant of Text:set, use Text:clear instead.
|
* Removed the no-argument variant of Text:set, use Text:clear instead.
|
||||||
* Removed Shader:getExternVariable, use Shader:hasUniform instead.
|
* Removed Shader:getExternVariable, use Shader:hasUniform instead.
|
||||||
|
* Removed love.graphics.newScreenshot, use love.graphics.captureScreenshot instead.
|
||||||
* Removed deprecated enet function host:socket_get_address.
|
* Removed deprecated enet function host:socket_get_address.
|
||||||
* Removed functions deprecated in LÖVE 0.10.2:
|
* Removed functions deprecated in LÖVE 0.10.2:
|
||||||
* Removed Shader:sendInt, Shader:sendBoolean, Shader:sentFloat, Shader:sendMatrix, and Shader:sendTexture (use Shader:send instead).
|
* Removed Shader:sendInt, Shader:sendBoolean, Shader:sentFloat, Shader:sendMatrix, and Shader:sendTexture (use Shader:send instead).
|
||||||
|
|||||||
@@ -157,11 +157,11 @@ void Event::exceptionIfInRenderPass()
|
|||||||
{
|
{
|
||||||
// Some core OS graphics functionality (e.g. swap buffers on some platforms)
|
// Some core OS graphics functionality (e.g. swap buffers on some platforms)
|
||||||
// happens inside SDL_PumpEvents - which is called by SDL_PollEvent and
|
// happens inside SDL_PumpEvents - which is called by SDL_PollEvent and
|
||||||
// friends. It's probably a bad idea to call those functions while we're in
|
// friends. It's probably a bad idea to call those functions while a Canvas
|
||||||
// a render pass.
|
// is active.
|
||||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||||
if (gfx != nullptr && gfx->isPassActive())
|
if (gfx != nullptr && gfx->isCanvasActive())
|
||||||
throw love::Exception("Cannot call this function while a render pass is active in love.graphics.");
|
throw love::Exception("Cannot call this function while a Canvas is active in love.graphics.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Message *Event::convert(const SDL_Event &e)
|
Message *Event::convert(const SDL_Event &e)
|
||||||
|
|||||||
@@ -61,14 +61,6 @@ void gammaCorrectColor(Colorf &c);
|
|||||||
**/
|
**/
|
||||||
void unGammaCorrectColor(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
|
class Graphics : public Module
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -193,7 +185,7 @@ public:
|
|||||||
struct Stats
|
struct Stats
|
||||||
{
|
{
|
||||||
int drawCalls;
|
int drawCalls;
|
||||||
int renderPasses;
|
int canvasSwitches;
|
||||||
int shaderSwitches;
|
int shaderSwitches;
|
||||||
int canvases;
|
int canvases;
|
||||||
int images;
|
int images;
|
||||||
@@ -224,6 +216,12 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OptionalColorf
|
||||||
|
{
|
||||||
|
Colorf c;
|
||||||
|
bool enabled;
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~Graphics();
|
virtual ~Graphics();
|
||||||
|
|
||||||
// Implements Module.
|
// Implements Module.
|
||||||
@@ -261,7 +259,7 @@ public:
|
|||||||
**/
|
**/
|
||||||
virtual bool isActive() const = 0;
|
virtual bool isActive() const = 0;
|
||||||
|
|
||||||
virtual bool isPassActive() const = 0;
|
virtual bool isCanvasActive() const = 0;
|
||||||
|
|
||||||
static bool getConstant(const char *in, DrawMode &out);
|
static bool getConstant(const char *in, DrawMode &out);
|
||||||
static bool getConstant(DrawMode in, const char *&out);
|
static bool getConstant(DrawMode in, const char *&out);
|
||||||
|
|||||||
@@ -253,15 +253,8 @@ void Canvas::unloadVolatile()
|
|||||||
void Canvas::drawv(const Matrix4 &t, const Vertex *v)
|
void Canvas::drawv(const Matrix4 &t, const Vertex *v)
|
||||||
{
|
{
|
||||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
if (gfx != nullptr)
|
if (gfx != nullptr && gfx->isCanvasActive(this))
|
||||||
{
|
throw love::Exception("Cannot render a Canvas to itself!");
|
||||||
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");
|
OpenGL::TempDebugGroup debuggroup("Canvas draw");
|
||||||
|
|
||||||
@@ -334,6 +327,59 @@ const void *Canvas::getHandle() const
|
|||||||
return &texture;
|
return &texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getWidth() || (y + h) > getHeight())
|
||||||
|
throw love::Exception("Invalid rectangle dimensions.");
|
||||||
|
|
||||||
|
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
|
|
||||||
|
if (gfx != nullptr)
|
||||||
|
{
|
||||||
|
for (const auto &c : gfx->getCanvas())
|
||||||
|
{
|
||||||
|
if (c == this)
|
||||||
|
throw love::Exception("Canvas:newImageData cannot be called while that Canvas is currently active.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelFormat dataformat;
|
||||||
|
switch (getPixelFormat())
|
||||||
|
{
|
||||||
|
case PIXELFORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES
|
||||||
|
dataformat = PIXELFORMAT_RGBA16;
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_R16F:
|
||||||
|
case PIXELFORMAT_RG16F:
|
||||||
|
case PIXELFORMAT_RGBA16F:
|
||||||
|
case PIXELFORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES
|
||||||
|
dataformat = PIXELFORMAT_RGBA16F;
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_R32F:
|
||||||
|
case PIXELFORMAT_RG32F:
|
||||||
|
case PIXELFORMAT_RGBA32F:
|
||||||
|
dataformat = PIXELFORMAT_RGBA32F;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dataformat = PIXELFORMAT_RGBA8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
love::image::ImageData *imagedata = module->newImageData(w, h, dataformat);
|
||||||
|
|
||||||
|
bool isSRGB = false;
|
||||||
|
OpenGL::TextureFormat fmt = gl.convertPixelFormat(dataformat, false, isSRGB);
|
||||||
|
|
||||||
|
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
|
||||||
|
|
||||||
|
glReadPixels(x, y, w, h, fmt.externalformat, fmt.type, imagedata->getData());
|
||||||
|
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||||
|
|
||||||
|
return imagedata;
|
||||||
|
}
|
||||||
|
|
||||||
PixelFormat Canvas::getSizedFormat(PixelFormat format)
|
PixelFormat Canvas::getSizedFormat(PixelFormat format)
|
||||||
{
|
{
|
||||||
switch (format)
|
switch (format)
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ public:
|
|||||||
bool setWrap(const Texture::Wrap &w) override;
|
bool setWrap(const Texture::Wrap &w) override;
|
||||||
const void *getHandle() const override;
|
const void *getHandle() const override;
|
||||||
|
|
||||||
|
love::image::ImageData *newImageData(love::image::Image *module, int x, int y, int w, int h);
|
||||||
|
|
||||||
inline GLenum getStatus() const
|
inline GLenum getStatus() const
|
||||||
{
|
{
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
@@ -58,10 +58,8 @@ Graphics::Graphics()
|
|||||||
, height(0)
|
, height(0)
|
||||||
, created(false)
|
, created(false)
|
||||||
, active(true)
|
, active(true)
|
||||||
, canCaptureScreenshot(true)
|
|
||||||
, currentPass()
|
|
||||||
, writingToStencil(false)
|
, writingToStencil(false)
|
||||||
, renderPassCount(0)
|
, canvasSwitchCount(0)
|
||||||
{
|
{
|
||||||
gl = OpenGL();
|
gl = OpenGL();
|
||||||
|
|
||||||
@@ -132,6 +130,7 @@ void Graphics::restoreState(const DisplayState &s)
|
|||||||
|
|
||||||
setFont(s.font.get());
|
setFont(s.font.get());
|
||||||
setShader(s.shader.get());
|
setShader(s.shader.get());
|
||||||
|
setCanvas(s.canvases);
|
||||||
|
|
||||||
setColorMask(s.colorMask);
|
setColorMask(s.colorMask);
|
||||||
setWireframe(s.wireframe);
|
setWireframe(s.wireframe);
|
||||||
@@ -174,6 +173,22 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
|||||||
setFont(s.font.get());
|
setFont(s.font.get());
|
||||||
setShader(s.shader.get());
|
setShader(s.shader.get());
|
||||||
|
|
||||||
|
bool canvaseschanged = s.canvases.size() != cur.canvases.size();
|
||||||
|
if (!canvaseschanged)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < s.canvases.size() && i < cur.canvases.size(); i++)
|
||||||
|
{
|
||||||
|
if (s.canvases[i].get() != cur.canvases[i].get())
|
||||||
|
{
|
||||||
|
canvaseschanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvaseschanged)
|
||||||
|
setCanvas(s.canvases);
|
||||||
|
|
||||||
if (s.colorMask != cur.colorMask)
|
if (s.colorMask != cur.colorMask)
|
||||||
setColorMask(s.colorMask);
|
setColorMask(s.colorMask);
|
||||||
|
|
||||||
@@ -211,7 +226,7 @@ void Graphics::setViewportSize(int width, int height)
|
|||||||
this->width = width;
|
this->width = width;
|
||||||
this->height = height;
|
this->height = height;
|
||||||
|
|
||||||
if (currentPass.active && currentPass.info.colorAttachmentCount == 0)
|
if (states.back().canvases.empty())
|
||||||
{
|
{
|
||||||
// Set the viewport to top-left corner.
|
// Set the viewport to top-left corner.
|
||||||
gl.setViewport({0, 0, width, height}, false);
|
gl.setViewport({0, 0, width, height}, false);
|
||||||
@@ -427,97 +442,72 @@ void Graphics::reset()
|
|||||||
origin();
|
origin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::beginPass(PassInfo::BeginAction beginAction, Colorf clearColor)
|
void Graphics::setCanvas(Canvas *canvas)
|
||||||
{
|
{
|
||||||
if (currentPass.active)
|
if (canvas == nullptr)
|
||||||
throw love::Exception("Cannot call beginPass while another render pass is active!");
|
return setCanvas();
|
||||||
|
|
||||||
currentPass.active = true;
|
std::vector<Canvas *> canvases = {canvas};
|
||||||
|
setCanvas(canvases);
|
||||||
OpenGL::TempDebugGroup debuggroup("Render Pass begin");
|
|
||||||
|
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
|
||||||
|
|
||||||
gl.setViewport({0, 0, width, height}, false);
|
|
||||||
|
|
||||||
// The projection matrix is flipped compared to rendering to a canvas, due
|
|
||||||
// to OpenGL considering (0,0) bottom-left instead of top-left.
|
|
||||||
gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0);
|
|
||||||
|
|
||||||
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
|
|
||||||
{
|
|
||||||
if (isGammaCorrect() && !gl.hasFramebufferSRGB())
|
|
||||||
gl.setFramebufferSRGB(true);
|
|
||||||
else if (!isGammaCorrect() && gl.hasFramebufferSRGB())
|
|
||||||
gl.setFramebufferSRGB(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always clear the stencil buffer, for now.
|
|
||||||
GLbitfield clearflags = GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
|
||||||
|
|
||||||
if (beginAction == PassInfo::BEGIN_CLEAR)
|
|
||||||
{
|
|
||||||
Colorf c = clearColor;
|
|
||||||
gammaCorrectColor(c);
|
|
||||||
|
|
||||||
glClearColor(c.r, c.g, c.b, c.a);
|
|
||||||
clearflags |= GL_COLOR_BUFFER_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clearflags != 0)
|
|
||||||
glClear(clearflags);
|
|
||||||
|
|
||||||
PassInfo info;
|
|
||||||
info.colorAttachmentCount = 0;
|
|
||||||
info.stencil = true;
|
|
||||||
|
|
||||||
currentPass.info = info;
|
|
||||||
|
|
||||||
renderPassCount++;
|
|
||||||
|
|
||||||
canCaptureScreenshot = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::beginPass(const PassInfo &info)
|
void Graphics::setCanvas(const std::vector<Canvas *> &canvases)
|
||||||
{
|
{
|
||||||
if (info.colorAttachmentCount == 0)
|
DisplayState &state = states.back();
|
||||||
throw love::Exception("At least one Canvas must be specified for an off-screen render pass.");
|
int ncanvases = (int) canvases.size();
|
||||||
|
|
||||||
if (currentPass.active)
|
if (ncanvases == 0)
|
||||||
throw love::Exception("Cannot call beginPass while another render pass is active!");
|
return setCanvas();
|
||||||
|
|
||||||
if (info.colorAttachmentCount > gl.getMaxRenderTargets())
|
if (ncanvases == (int) state.canvases.size())
|
||||||
throw love::Exception("This system can't simultaneously render to %d canvases.", info.colorAttachmentCount);
|
{
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
Canvas *firstcanvas = info.colorAttachments[0].canvas;
|
for (int i = 0; i < ncanvases; i++)
|
||||||
|
{
|
||||||
|
if (canvases[i] != state.canvases[i].get())
|
||||||
|
{
|
||||||
|
modified = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool multiformatsupported = isSupported(Feature::FEATURE_MULTI_CANVAS_FORMATS);
|
if (!modified)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ncanvases > gl.getMaxRenderTargets())
|
||||||
|
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
|
||||||
|
|
||||||
|
Canvas *firstcanvas = canvases[0];
|
||||||
|
|
||||||
|
bool multiformatsupported = Canvas::isMultiFormatMultiCanvasSupported();
|
||||||
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
||||||
|
|
||||||
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
|
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
|
||||||
|
|
||||||
for (int i = 1; i < info.colorAttachmentCount; i++)
|
for (int i = 1; i < ncanvases; i++)
|
||||||
{
|
{
|
||||||
Canvas *c = info.colorAttachments[i].canvas;
|
Canvas *c = canvases[i];
|
||||||
|
|
||||||
if (c->getWidth() != firstcanvas->getWidth() || c->getHeight() != firstcanvas->getHeight())
|
if (c->getWidth() != firstcanvas->getWidth() || c->getHeight() != firstcanvas->getHeight())
|
||||||
throw love::Exception("All canvases in a render pass must have the same dimensions.");
|
throw love::Exception("All canvases in must have the same dimensions.");
|
||||||
|
|
||||||
if (!multiformatsupported && c->getPixelFormat() != firstformat)
|
if (!multiformatsupported && c->getPixelFormat() != firstformat)
|
||||||
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
||||||
|
|
||||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||||
throw love::Exception("All Canvases in a render pass must have the same requested MSAA value.");
|
throw love::Exception("All Canvases in must have the same requested MSAA value.");
|
||||||
|
|
||||||
if (c->getPixelFormat() == PIXELFORMAT_sRGBA8)
|
if (c->getPixelFormat() == PIXELFORMAT_sRGBA8)
|
||||||
hasSRGBcanvas = true;
|
hasSRGBcanvas = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Render Pass begin");
|
OpenGL::TempDebugGroup debuggroup("setCanvas(...)");
|
||||||
|
|
||||||
bindCachedFBOForPass(info);
|
endPass();
|
||||||
|
|
||||||
currentPass.active = true;
|
bindCachedFBO(canvases);
|
||||||
|
|
||||||
int w = firstcanvas->getWidth();
|
int w = firstcanvas->getWidth();
|
||||||
int h = firstcanvas->getHeight();
|
int h = firstcanvas->getHeight();
|
||||||
@@ -534,73 +524,102 @@ void Graphics::beginPass(const PassInfo &info)
|
|||||||
gl.setFramebufferSRGB(false);
|
gl.setFramebufferSRGB(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLbitfield clearflags = 0;
|
std::vector<StrongRef<Canvas>> canvasrefs;
|
||||||
|
canvasrefs.reserve(canvases.size());
|
||||||
|
|
||||||
// Take a single-color clear codepath if there's only one specified Canvas.
|
for (Canvas *c : canvases)
|
||||||
// The multi-color codepath (in the loop below) assumes MRT functions are
|
canvasrefs.push_back(c);
|
||||||
// available, and also may call more expensive GL functions which are
|
|
||||||
// unnecessary if only one Canvas is used.
|
std::swap(state.canvases, canvasrefs);
|
||||||
if (info.colorAttachmentCount <= 1)
|
|
||||||
|
canvasSwitchCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::setCanvas(const std::vector<StrongRef<Canvas>> &canvases)
|
||||||
|
{
|
||||||
|
std::vector<Canvas *> canvaslist;
|
||||||
|
canvaslist.reserve(canvases.size());
|
||||||
|
|
||||||
|
for (const StrongRef<Canvas> &c : canvases)
|
||||||
|
canvaslist.push_back(c.get());
|
||||||
|
|
||||||
|
return setCanvas(canvaslist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::setCanvas()
|
||||||
|
{
|
||||||
|
DisplayState &state = states.back();
|
||||||
|
|
||||||
|
if (state.canvases.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
OpenGL::TempDebugGroup debuggroup("setCanvas()");
|
||||||
|
|
||||||
|
endPass();
|
||||||
|
|
||||||
|
state.canvases.clear();
|
||||||
|
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||||
|
gl.setViewport({0, 0, width, height}, false);
|
||||||
|
|
||||||
|
// The projection matrix is flipped compared to rendering to a canvas, due
|
||||||
|
// to OpenGL considering (0,0) bottom-left instead of top-left.
|
||||||
|
gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0);
|
||||||
|
|
||||||
|
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
|
||||||
{
|
{
|
||||||
if (info.colorAttachmentCount > 0 && info.colorAttachments[0].beginAction == PassInfo::BEGIN_CLEAR)
|
if (isGammaCorrect() && !gl.hasFramebufferSRGB())
|
||||||
{
|
gl.setFramebufferSRGB(true);
|
||||||
clearflags |= GL_COLOR_BUFFER_BIT;
|
else if (!isGammaCorrect() && gl.hasFramebufferSRGB())
|
||||||
Colorf c = info.colorAttachments[0].clearColor;
|
gl.setFramebufferSRGB(false);
|
||||||
gammaCorrectColor(c);
|
|
||||||
glClearColor(c.r, c.g, c.b, c.a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool drawbuffermodified = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < info.colorAttachmentCount; i++)
|
|
||||||
{
|
|
||||||
if (info.colorAttachments[i].beginAction == PassInfo::BEGIN_CLEAR)
|
|
||||||
{
|
|
||||||
Colorf c = info.colorAttachments[i].clearColor;
|
|
||||||
gammaCorrectColor(c);
|
|
||||||
|
|
||||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0)
|
|
||||||
{
|
|
||||||
const GLfloat carray[] = {c.r, c.g, c.b, c.a};
|
|
||||||
glClearBufferfv(GL_COLOR, i, carray);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glDrawBuffer(GL_COLOR_ATTACHMENT0 + i);
|
|
||||||
glClearColor(c.r, c.g, c.b, c.a);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
drawbuffermodified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert to the expected draw buffers once we're done, if glClearBuffer
|
|
||||||
// wasn't supported.
|
|
||||||
if (drawbuffermodified)
|
|
||||||
{
|
|
||||||
std::vector<GLenum> bufs;
|
|
||||||
|
|
||||||
for (int i = 0; i < info.colorAttachmentCount; i++)
|
|
||||||
bufs.push_back(GL_COLOR_ATTACHMENT0 + i);
|
|
||||||
|
|
||||||
glDrawBuffers((int) bufs.size(), &bufs[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.stencil)
|
canvasSwitchCount++;
|
||||||
clearflags |= GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
}
|
||||||
|
|
||||||
if (clearflags != 0)
|
std::vector<Canvas *> Graphics::getCanvas() const
|
||||||
glClear(clearflags);
|
{
|
||||||
|
std::vector<Canvas *> canvases;
|
||||||
|
canvases.reserve(states.back().canvases.size());
|
||||||
|
|
||||||
for (int i = 0; i < info.colorAttachmentCount; i++)
|
for (const StrongRef<Canvas> &c : states.back().canvases)
|
||||||
info.colorAttachments[i].canvas->retain();
|
canvases.push_back(c.get());
|
||||||
|
|
||||||
currentPass.info = info;
|
return canvases;
|
||||||
renderPassCount++;
|
}
|
||||||
|
|
||||||
|
void Graphics::endPass()
|
||||||
|
{
|
||||||
|
// Discard the stencil buffer.
|
||||||
|
discard({}, true);
|
||||||
|
|
||||||
|
auto &canvases = states.back().canvases;
|
||||||
|
|
||||||
|
// Resolve MSAA buffers.
|
||||||
|
if (canvases.size() > 0 && canvases[0]->getMSAA() > 1)
|
||||||
|
{
|
||||||
|
int w = canvases[0]->getWidth();
|
||||||
|
int h = canvases[0]->getHeight();
|
||||||
|
|
||||||
|
for (int i = 0; i < (int) canvases.size(); i++)
|
||||||
|
{
|
||||||
|
glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
|
||||||
|
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, canvases[i]->getFBO());
|
||||||
|
|
||||||
|
if (GLAD_APPLE_framebuffer_multisample)
|
||||||
|
glResolveMultisampleFramebufferAPPLE();
|
||||||
|
else
|
||||||
|
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::clear(Colorf c)
|
||||||
|
{
|
||||||
|
gammaCorrectColor(c);
|
||||||
|
glClearColor(c.r, c.g, c.b, c.a);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current)
|
if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current)
|
||||||
{
|
{
|
||||||
@@ -611,123 +630,89 @@ void Graphics::beginPass(const PassInfo &info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::endPass()
|
void Graphics::clear(const std::vector<OptionalColorf> &colors)
|
||||||
{
|
{
|
||||||
endPass(0, 0, 0, 0, nullptr, nullptr);
|
if (colors.size() == 0)
|
||||||
}
|
return;
|
||||||
|
|
||||||
void Graphics::endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData)
|
int ncanvases = (int) states.back().canvases.size();
|
||||||
{
|
int ncolors = std::min((int) colors.size(), ncanvases);
|
||||||
if (!currentPass.active)
|
|
||||||
return; // Should this error instead?
|
|
||||||
|
|
||||||
StrongRef<love::image::ImageData> imagedata;
|
if (ncolors <= 1 && ncanvases <= 1)
|
||||||
|
|
||||||
auto &attachments = currentPass.info.colorAttachments;
|
|
||||||
int attachmentcount = currentPass.info.colorAttachmentCount;
|
|
||||||
|
|
||||||
if (info != nullptr)
|
|
||||||
{
|
{
|
||||||
if (attachmentcount == 0)
|
if (colors[0].enabled)
|
||||||
throw love::Exception("Use captureScreenshot to capture the main screen's contents.");
|
clear(colors[0].c);
|
||||||
|
|
||||||
if (sX < 0 || sY < 0 || sW <= 0 || sH <= 0 || (sX + sW) > getPassWidth() || (sY + sH) > getPassHeight())
|
return;
|
||||||
throw love::Exception("Invalid rectangle dimensions.");
|
|
||||||
|
|
||||||
auto imagemodule = Module::getInstance<image::Image>(M_IMAGE);
|
|
||||||
|
|
||||||
if (imagemodule == nullptr)
|
|
||||||
throw love::Exception("The love.image module must be loaded to capture a Canvas' contents.");
|
|
||||||
|
|
||||||
PixelFormat format;
|
|
||||||
switch (attachments[0].canvas->getPixelFormat())
|
|
||||||
{
|
|
||||||
case PIXELFORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES
|
|
||||||
format = PIXELFORMAT_RGBA16;
|
|
||||||
break;
|
|
||||||
case PIXELFORMAT_R16F:
|
|
||||||
case PIXELFORMAT_RG16F:
|
|
||||||
case PIXELFORMAT_RGBA16F:
|
|
||||||
case PIXELFORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES
|
|
||||||
format = PIXELFORMAT_RGBA16F;
|
|
||||||
break;
|
|
||||||
case PIXELFORMAT_R32F:
|
|
||||||
case PIXELFORMAT_RG32F:
|
|
||||||
case PIXELFORMAT_RGBA32F:
|
|
||||||
format = PIXELFORMAT_RGBA32F;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = PIXELFORMAT_RGBA8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
imagedata.set(imagemodule->newImageData(sW, sH, format), Acquire::NORETAIN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPass.info.stencil)
|
bool drawbuffersmodified = false;
|
||||||
discard(OpenGL::FRAMEBUFFER_ALL, {}, true);
|
|
||||||
|
|
||||||
if (attachmentcount > 0 && attachments[0].canvas->getMSAA() > 1)
|
for (int i = 0; i < ncolors; i++)
|
||||||
{
|
{
|
||||||
int w = attachments[0].canvas->getWidth();
|
if (!colors[i].enabled)
|
||||||
int h = attachments[0].canvas->getHeight();
|
continue;
|
||||||
|
|
||||||
for (int i = 0; i < attachmentcount; i++)
|
Colorf c = colors[i].c;
|
||||||
|
gammaCorrectColor(c);
|
||||||
|
|
||||||
|
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0)
|
||||||
{
|
{
|
||||||
glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
|
const GLfloat carray[] = {c.r, c.g, c.b, c.a};
|
||||||
|
glClearBufferfv(GL_COLOR, i, carray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDrawBuffer(GL_COLOR_ATTACHMENT0 + i);
|
||||||
|
glClearColor(c.r, c.g, c.b, c.a);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, attachments[i].canvas->getFBO());
|
drawbuffersmodified = true;
|
||||||
|
|
||||||
if (GLAD_APPLE_framebuffer_multisample)
|
|
||||||
glResolveMultisampleFramebufferAPPLE();
|
|
||||||
else
|
|
||||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info != nullptr)
|
// Revert to the expected draw buffers once we're done, if glClearBuffer
|
||||||
|
// wasn't supported.
|
||||||
|
if (drawbuffersmodified)
|
||||||
{
|
{
|
||||||
if (attachments[0].canvas->getMSAA() > 1)
|
GLenum bufs[MAX_COLOR_RENDER_TARGETS];
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, attachments[0].canvas->getFBO());
|
|
||||||
else if (attachmentcount > 1)
|
|
||||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
||||||
|
|
||||||
GLenum datatype;
|
for (int i = 0; i < ncanvases; i++)
|
||||||
switch (imagedata->getFormat())
|
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||||
{
|
|
||||||
case PIXELFORMAT_RGBA16:
|
|
||||||
datatype = GL_UNSIGNED_SHORT;
|
|
||||||
break;
|
|
||||||
case PIXELFORMAT_RGBA16F:
|
|
||||||
datatype = GL_HALF_FLOAT;
|
|
||||||
break;
|
|
||||||
case PIXELFORMAT_RGBA32F:
|
|
||||||
datatype = GL_FLOAT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
datatype = GL_UNSIGNED_BYTE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
glReadPixels(sX, sY, sW, sH, GL_RGBA, datatype, imagedata->getData());
|
glDrawBuffers(ncanvases, bufs);
|
||||||
|
|
||||||
info->callback(imagedata, info->ref, screenshotCallbackData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < currentPass.info.colorAttachmentCount; i++)
|
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
currentPass.info.colorAttachments[i].canvas->release();
|
|
||||||
|
|
||||||
currentPass.active = false;
|
if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current)
|
||||||
|
{
|
||||||
|
// This seems to be enough to fix the bug for me. Other methods I've
|
||||||
|
// tried (e.g. dummy draws) don't work in all cases.
|
||||||
|
gl.useProgram(0);
|
||||||
|
gl.useProgram(Shader::current->getProgram());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PassInfo &Graphics::getActivePass() const
|
bool Graphics::isCanvasActive() const
|
||||||
{
|
{
|
||||||
return currentPass.info;
|
return !states.back().canvases.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Graphics::isPassActive() const
|
bool Graphics::isCanvasActive(Canvas *canvas) const
|
||||||
{
|
{
|
||||||
return currentPass.active;
|
for (const auto &c : states.back().canvases)
|
||||||
|
{
|
||||||
|
if (c.get() == canvas)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
|
||||||
|
{
|
||||||
|
discard(OpenGL::FRAMEBUFFER_ALL, colorbuffers, depthstencil);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil)
|
void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil)
|
||||||
@@ -745,7 +730,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
attachments.reserve(colorbuffers.size());
|
attachments.reserve(colorbuffers.size());
|
||||||
|
|
||||||
// glDiscardFramebuffer uses different attachment enums for the default FBO.
|
// glDiscardFramebuffer uses different attachment enums for the default FBO.
|
||||||
if (currentPass.info.colorAttachmentCount == 0 && gl.getDefaultFBO() == 0)
|
if (states.back().canvases.empty() && gl.getDefaultFBO() == 0)
|
||||||
{
|
{
|
||||||
if (colorbuffers.size() > 0 && colorbuffers[0])
|
if (colorbuffers.size() > 0 && colorbuffers[0])
|
||||||
attachments.push_back(GL_COLOR);
|
attachments.push_back(GL_COLOR);
|
||||||
@@ -758,7 +743,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int rendertargetcount = std::max(currentPass.info.colorAttachmentCount, 1);
|
int rendertargetcount = std::max((int) states.back().canvases.size(), 1);
|
||||||
|
|
||||||
for (int i = 0; i < (int) colorbuffers.size(); i++)
|
for (int i = 0; i < (int) colorbuffers.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -780,19 +765,11 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
|||||||
glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
|
glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::bindCachedFBOForPass(const PassInfo &pass)
|
void Graphics::bindCachedFBO(const std::vector<Canvas *> &canvases)
|
||||||
{
|
{
|
||||||
PassBufferInfo info;
|
int ncanvases = (int) canvases.size();
|
||||||
memset(&info, 0, sizeof(PassBufferInfo));
|
|
||||||
|
|
||||||
info.stencil = pass.stencil;
|
uint32 hash = XXH32(&canvases[0], sizeof(Canvas *) * ncanvases, 0);
|
||||||
|
|
||||||
int ncanvases = pass.colorAttachmentCount;
|
|
||||||
|
|
||||||
for (int i = 0; i < ncanvases; i++)
|
|
||||||
info.canvases[i] = pass.colorAttachments[i].canvas;
|
|
||||||
|
|
||||||
uint32 hash = XXH32(&info, offsetof(PassBufferInfo, canvases) + ncanvases * sizeof(Canvas *), 0);
|
|
||||||
|
|
||||||
GLuint fbo = framebufferObjects[hash];
|
GLuint fbo = framebufferObjects[hash];
|
||||||
|
|
||||||
@@ -802,36 +779,35 @@ void Graphics::bindCachedFBOForPass(const PassInfo &pass)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int w = info.canvases[0]->getWidth();
|
int w = canvases[0]->getWidth();
|
||||||
int h = info.canvases[0]->getHeight();
|
int h = canvases[0]->getHeight();
|
||||||
int msaa = std::max(info.canvases[0]->getMSAA(), 1);
|
int msaa = std::max(canvases[0]->getMSAA(), 1);
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo);
|
glGenFramebuffers(1, &fbo);
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
||||||
|
|
||||||
std::vector<GLenum> drawbuffers;
|
GLenum drawbuffers[MAX_COLOR_RENDER_TARGETS];
|
||||||
drawbuffers.reserve(ncanvases);
|
|
||||||
|
|
||||||
for (int i = 0; i < ncanvases; i++)
|
for (int i = 0; i < ncanvases; i++)
|
||||||
{
|
{
|
||||||
drawbuffers.push_back(GL_COLOR_ATTACHMENT0 + i);
|
drawbuffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||||
|
|
||||||
if (msaa > 1)
|
if (msaa > 1)
|
||||||
{
|
{
|
||||||
GLuint rbo = (GLuint) info.canvases[i]->getMSAAHandle();
|
GLuint rbo = (GLuint) canvases[i]->getMSAAHandle();
|
||||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo);
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GLuint tex = *(GLuint *) info.canvases[i]->getHandle();
|
GLuint tex = *(GLuint *) canvases[i]->getHandle();
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, drawbuffers[i], GL_TEXTURE_2D, tex, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, drawbuffers[i], GL_TEXTURE_2D, tex, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawbuffers.size() > 1)
|
if (ncanvases > 1)
|
||||||
glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
|
glDrawBuffers(ncanvases, drawbuffers);
|
||||||
|
|
||||||
GLuint stencil = attachCachedStencilBuffer(w, h, info.canvases[0]->getRequestedMSAA());
|
GLuint stencil = attachCachedStencilBuffer(w, h, canvases[0]->getRequestedMSAA());
|
||||||
|
|
||||||
if (stencil == 0)
|
if (stencil == 0)
|
||||||
{
|
{
|
||||||
@@ -872,7 +848,7 @@ GLuint Graphics::attachCachedStencilBuffer(int w, int h, int samples)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Created cached stencil buffer");
|
OpenGL::TempDebugGroup debuggroup("Create cached stencil buffer");
|
||||||
|
|
||||||
CachedRenderbuffer rb;
|
CachedRenderbuffer rb;
|
||||||
rb.w = w;
|
rb.w = w;
|
||||||
@@ -921,16 +897,16 @@ GLuint Graphics::attachCachedStencilBuffer(int w, int h, int samples)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rb.renderbuffer != 0)
|
if (rb.renderbuffer != 0)
|
||||||
|
{
|
||||||
|
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
stencilBuffers.push_back(rb);
|
stencilBuffers.push_back(rb);
|
||||||
|
}
|
||||||
|
|
||||||
return rb.renderbuffer;
|
return rb.renderbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::captureScreenshot(const ScreenshotInfo &info)
|
void Graphics::captureScreenshot(const ScreenshotInfo &info)
|
||||||
{
|
{
|
||||||
if (!canCaptureScreenshot)
|
|
||||||
throw love::Exception("captureScreenshot cannot be called once rendering to the main screen has begun.");
|
|
||||||
|
|
||||||
pendingScreenshotCallbacks.push_back(info);
|
pendingScreenshotCallbacks.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,8 +915,10 @@ void Graphics::present(void *screenshotCallbackData)
|
|||||||
if (!isActive())
|
if (!isActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (currentPass.active)
|
if (!states.back().canvases.empty())
|
||||||
throw love::Exception("present cannot be called while a render pass is active.");
|
throw love::Exception("present cannot be called while a Canvas is active.");
|
||||||
|
|
||||||
|
endPass();
|
||||||
|
|
||||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||||
|
|
||||||
@@ -1049,9 +1027,7 @@ void Graphics::present(void *screenshotCallbackData)
|
|||||||
// Reset the per-frame stat counts.
|
// Reset the per-frame stat counts.
|
||||||
gl.stats.drawCalls = 0;
|
gl.stats.drawCalls = 0;
|
||||||
gl.stats.shaderSwitches = 0;
|
gl.stats.shaderSwitches = 0;
|
||||||
renderPassCount = 0;
|
canvasSwitchCount = 0;
|
||||||
|
|
||||||
canCaptureScreenshot = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Graphics::getWidth() const
|
int Graphics::getWidth() const
|
||||||
@@ -1064,22 +1040,6 @@ int Graphics::getHeight() const
|
|||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Graphics::getPassWidth() const
|
|
||||||
{
|
|
||||||
if (currentPass.active && currentPass.info.colorAttachmentCount > 0)
|
|
||||||
return currentPass.info.colorAttachments[0].canvas->getWidth();
|
|
||||||
else
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Graphics::getPassHeight() const
|
|
||||||
{
|
|
||||||
if (currentPass.active && currentPass.info.colorAttachmentCount > 0)
|
|
||||||
return currentPass.info.colorAttachments[0].canvas->getHeight();
|
|
||||||
else
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Graphics::isCreated() const
|
bool Graphics::isCreated() const
|
||||||
{
|
{
|
||||||
return created;
|
return created;
|
||||||
@@ -1087,12 +1047,14 @@ bool Graphics::isCreated() const
|
|||||||
|
|
||||||
void Graphics::setScissor(const Rect &rect)
|
void Graphics::setScissor(const Rect &rect)
|
||||||
{
|
{
|
||||||
|
DisplayState &state = states.back();
|
||||||
|
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
||||||
gl.setScissor({rect.x, rect.y, rect.w, rect.h}, currentPass.info.colorAttachmentCount > 0);
|
gl.setScissor(rect, !state.canvases.empty());
|
||||||
|
|
||||||
states.back().scissor = true;
|
state.scissor = true;
|
||||||
states.back().scissorRect = rect;
|
state.scissorRect = rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::intersectScissor(const Rect &rect)
|
void Graphics::intersectScissor(const Rect &rect)
|
||||||
@@ -1132,9 +1094,6 @@ bool Graphics::getScissor(Rect &rect) const
|
|||||||
|
|
||||||
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
||||||
{
|
{
|
||||||
if (!currentPass.active || !currentPass.info.stencil)
|
|
||||||
throw love::Exception("Stenciling must be enabled in the active render pass.");
|
|
||||||
|
|
||||||
writingToStencil = true;
|
writingToStencil = true;
|
||||||
|
|
||||||
// Disable color writes but don't save the state for it.
|
// Disable color writes but don't save the state for it.
|
||||||
@@ -1189,9 +1148,6 @@ void Graphics::stopDrawToStencilBuffer()
|
|||||||
|
|
||||||
void Graphics::setStencilTest(CompareMode compare, int value)
|
void Graphics::setStencilTest(CompareMode compare, int value)
|
||||||
{
|
{
|
||||||
if (!currentPass.info.stencil && compare != COMPARE_ALWAYS)
|
|
||||||
throw love::Exception("Stenciling must be enabled in the active render pass.");
|
|
||||||
|
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
state.stencilCompare = compare;
|
state.stencilCompare = compare;
|
||||||
state.stencilTestValue = value;
|
state.stencilTestValue = value;
|
||||||
@@ -1608,25 +1564,16 @@ bool Graphics::isWireframe() const
|
|||||||
|
|
||||||
void Graphics::draw(Drawable *drawable, const Matrix4 &m)
|
void Graphics::draw(Drawable *drawable, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
drawable->draw(m);
|
drawable->draw(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::drawq(Texture *texture, Quad *quad, const Matrix4 &m)
|
void Graphics::drawq(Texture *texture, Quad *quad, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
texture->drawq(quad, m);
|
texture->drawq(quad, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4 &m)
|
void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
@@ -1637,9 +1584,6 @@ void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4
|
|||||||
|
|
||||||
void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
|
void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
DisplayState &state = states.back();
|
DisplayState &state = states.back();
|
||||||
@@ -1654,9 +1598,6 @@ void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, F
|
|||||||
|
|
||||||
void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints)
|
void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Graphics points draw");
|
OpenGL::TempDebugGroup debuggroup("Graphics points draw");
|
||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
@@ -1678,9 +1619,6 @@ void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints
|
|||||||
|
|
||||||
void Graphics::polyline(const float *coords, size_t count)
|
void Graphics::polyline(const float *coords, size_t count)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
const DisplayState &state = states.back();
|
const DisplayState &state = states.back();
|
||||||
float pixelsize = 1.0f / std::max((float) pixelScaleStack.back(), 0.000001f);
|
float pixelsize = 1.0f / std::max((float) pixelScaleStack.back(), 0.000001f);
|
||||||
|
|
||||||
@@ -1712,9 +1650,6 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
|
|||||||
|
|
||||||
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points)
|
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
if (rx == 0 || ry == 0)
|
if (rx == 0 || ry == 0)
|
||||||
{
|
{
|
||||||
rectangle(mode, x, y, w, h);
|
rectangle(mode, x, y, w, h);
|
||||||
@@ -1798,9 +1733,6 @@ void Graphics::circle(DrawMode mode, float x, float y, float radius)
|
|||||||
|
|
||||||
void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points)
|
void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
float two_pi = static_cast<float>(LOVE_M_PI * 2);
|
float two_pi = static_cast<float>(LOVE_M_PI * 2);
|
||||||
if (points <= 0) points = 1;
|
if (points <= 0) points = 1;
|
||||||
float angle_shift = (two_pi / points);
|
float angle_shift = (two_pi / points);
|
||||||
@@ -1828,9 +1760,6 @@ void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b)
|
|||||||
|
|
||||||
void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2, int points)
|
void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2, int points)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
// Nothing to display with no points or equal angles. (Or is there with line mode?)
|
// Nothing to display with no points or equal angles. (Or is there with line mode?)
|
||||||
if (points <= 0 || angle1 == angle2)
|
if (points <= 0 || angle1 == angle2)
|
||||||
return;
|
return;
|
||||||
@@ -1924,9 +1853,6 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r
|
|||||||
/// @param count the number of coordinates/size of the array
|
/// @param count the number of coordinates/size of the array
|
||||||
void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
|
void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
|
||||||
{
|
{
|
||||||
if (!currentPass.active)
|
|
||||||
throw RenderOutsidePassException();
|
|
||||||
|
|
||||||
// coords is an array of a closed loop of vertices, i.e.
|
// coords is an array of a closed loop of vertices, i.e.
|
||||||
// coords[count-2] = coords[0], coords[count-1] = coords[1]
|
// coords[count-2] = coords[0], coords[count-1] = coords[1]
|
||||||
if (mode == DRAW_LINE)
|
if (mode == DRAW_LINE)
|
||||||
@@ -1981,7 +1907,7 @@ Graphics::Stats Graphics::getStats() const
|
|||||||
Stats stats;
|
Stats stats;
|
||||||
|
|
||||||
stats.drawCalls = gl.stats.drawCalls;
|
stats.drawCalls = gl.stats.drawCalls;
|
||||||
stats.renderPasses = renderPassCount;
|
stats.canvasSwitches = canvasSwitchCount;
|
||||||
stats.shaderSwitches = gl.stats.shaderSwitches;
|
stats.shaderSwitches = gl.stats.shaderSwitches;
|
||||||
stats.canvases = Canvas::canvasCount;
|
stats.canvases = Canvas::canvasCount;
|
||||||
stats.images = Image::imageCount;
|
stats.images = Image::imageCount;
|
||||||
|
|||||||
@@ -62,45 +62,6 @@ namespace graphics
|
|||||||
namespace opengl
|
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
|
class Graphics : public love::graphics::Graphics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -135,14 +96,13 @@ public:
|
|||||||
**/
|
**/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void beginPass(PassInfo::BeginAction beginAction, Colorf clearColor);
|
void clear(Colorf color);
|
||||||
void beginPass(const PassInfo &info);
|
void clear(const std::vector<OptionalColorf> &colors);
|
||||||
|
|
||||||
void endPass();
|
void discard(const std::vector<bool> &colorbuffers, bool depthstencil);
|
||||||
void endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData);
|
|
||||||
|
|
||||||
const PassInfo &getActivePass() const;
|
virtual bool isCanvasActive() const;
|
||||||
virtual bool isPassActive() const;
|
bool isCanvasActive(Canvas *canvas) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flips buffers. (Rendered geometry is presented on screen).
|
* Flips buffers. (Rendered geometry is presented on screen).
|
||||||
@@ -159,14 +119,18 @@ public:
|
|||||||
**/
|
**/
|
||||||
int getHeight() const;
|
int getHeight() const;
|
||||||
|
|
||||||
int getPassWidth() const;
|
|
||||||
int getPassHeight() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if a graphics viewport is set.
|
* True if a graphics viewport is set.
|
||||||
**/
|
**/
|
||||||
bool isCreated() const;
|
bool isCreated() 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scissor defines a box such that everything outside that box is discarded and not drawn.
|
* Scissor defines a box such that everything outside that box is discarded and not drawn.
|
||||||
* Scissoring is automatically enabled.
|
* Scissoring is automatically enabled.
|
||||||
@@ -519,6 +483,8 @@ private:
|
|||||||
StrongRef<Font> font;
|
StrongRef<Font> font;
|
||||||
StrongRef<Shader> shader;
|
StrongRef<Shader> shader;
|
||||||
|
|
||||||
|
std::vector<StrongRef<Canvas>> canvases;
|
||||||
|
|
||||||
ColorMask colorMask = ColorMask(true, true, true, true);
|
ColorMask colorMask = ColorMask(true, true, true, true);
|
||||||
|
|
||||||
bool wireframe = false;
|
bool wireframe = false;
|
||||||
@@ -529,18 +495,6 @@ private:
|
|||||||
float defaultMipmapSharpness = 0.0f;
|
float defaultMipmapSharpness = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CurrentPass
|
|
||||||
{
|
|
||||||
PassInfo info;
|
|
||||||
bool active = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PassBufferInfo
|
|
||||||
{
|
|
||||||
bool stencil;
|
|
||||||
Canvas *canvases[MAX_COLOR_RENDER_TARGETS];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CachedRenderbuffer
|
struct CachedRenderbuffer
|
||||||
{
|
{
|
||||||
int w;
|
int w;
|
||||||
@@ -553,7 +507,8 @@ private:
|
|||||||
void restoreState(const DisplayState &s);
|
void restoreState(const DisplayState &s);
|
||||||
void restoreStateChecked(const DisplayState &s);
|
void restoreStateChecked(const DisplayState &s);
|
||||||
|
|
||||||
void bindCachedFBOForPass(const PassInfo &pass);
|
void endPass();
|
||||||
|
void bindCachedFBO(const std::vector<Canvas *> &canvases);
|
||||||
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
||||||
GLuint attachCachedStencilBuffer(int w, int h, int samples);
|
GLuint attachCachedStencilBuffer(int w, int h, int samples);
|
||||||
|
|
||||||
@@ -577,16 +532,12 @@ private:
|
|||||||
bool created;
|
bool created;
|
||||||
bool active;
|
bool active;
|
||||||
|
|
||||||
bool canCaptureScreenshot;
|
|
||||||
|
|
||||||
CurrentPass currentPass;
|
|
||||||
|
|
||||||
bool writingToStencil;
|
bool writingToStencil;
|
||||||
|
|
||||||
std::vector<DisplayState> states;
|
std::vector<DisplayState> states;
|
||||||
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
|
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
|
||||||
|
|
||||||
int renderPassCount;
|
int canvasSwitchCount;
|
||||||
|
|
||||||
static const size_t MAX_USER_STACK_DEPTH = 64;
|
static const size_t MAX_USER_STACK_DEPTH = 64;
|
||||||
|
|
||||||
|
|||||||
@@ -806,7 +806,7 @@ void Shader::checkSetScreenParams()
|
|||||||
Rect view = gl.getViewport();
|
Rect view = gl.getViewport();
|
||||||
|
|
||||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
bool canvasActive = gfx->getActivePass().colorAttachmentCount > 0;
|
bool canvasActive = gfx->isCanvasActive();
|
||||||
|
|
||||||
if (view == lastViewport && canvasWasActive == canvasActive)
|
if (view == lastViewport && canvasWasActive == canvasActive)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -52,10 +52,61 @@ int w_Canvas_getMSAA(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, love::image::Image::type);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg w_Canvas_functions[] =
|
static const luaL_Reg w_Canvas_functions[] =
|
||||||
{
|
{
|
||||||
{ "getFormat", w_Canvas_getFormat },
|
{ "getFormat", w_Canvas_getFormat },
|
||||||
{ "getMSAA", w_Canvas_getMSAA },
|
{ "getMSAA", w_Canvas_getMSAA },
|
||||||
|
{ "renderTo", w_Canvas_renderTo },
|
||||||
|
{ "newImageData", w_Canvas_newImageData },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,76 @@ int w_reset(lua_State *)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_clear(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].c.r = (float) luaL_checknumber(L, -4);
|
||||||
|
colors[i].c.g = (float) luaL_checknumber(L, -3);
|
||||||
|
colors[i].c.b = (float) luaL_checknumber(L, -2);
|
||||||
|
colors[i].c.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 *L)
|
int w_present(lua_State *L)
|
||||||
{
|
{
|
||||||
luax_catchexcept(L, [&]() { instance()->present(L); });
|
luax_catchexcept(L, [&]() { instance()->present(L); });
|
||||||
@@ -105,149 +175,64 @@ int w_getDimensions(lua_State *L)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_getPassWidth(lua_State *L)
|
int w_setCanvas(lua_State *L)
|
||||||
{
|
{
|
||||||
lua_pushinteger(L, instance()->getPassWidth());
|
// Disable stencil writes.
|
||||||
return 1;
|
instance()->stopDrawToStencilBuffer();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
// called with none -> reset to default buffer
|
||||||
if (lua_isnoneornil(L, 1))
|
if (lua_isnoneornil(L, 1))
|
||||||
{
|
{
|
||||||
luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_LOAD, Colorf()); });
|
instance()->setCanvas();
|
||||||
nextstartidx = 1;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (lua_isnumber(L, 1))
|
|
||||||
|
bool is_table = lua_istable(L, 1);
|
||||||
|
std::vector<Canvas *> canvases;
|
||||||
|
|
||||||
|
if (is_table)
|
||||||
{
|
{
|
||||||
Colorf c;
|
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
|
||||||
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);
|
lua_rawgeti(L, 1, i);
|
||||||
nextstartidx = 5;
|
canvases.push_back(luax_checkcanvas(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
for (int i = 1; i <= lua_gettop(L); i++)
|
||||||
|
canvases.push_back(luax_checkcanvas(L, i));
|
||||||
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;
|
luax_catchexcept(L, [&]() {
|
||||||
|
if (canvases.size() > 0)
|
||||||
|
instance()->setCanvas(canvases);
|
||||||
|
else
|
||||||
|
instance()->setCanvas();
|
||||||
|
});
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_beginPass(lua_State *L)
|
int w_getCanvas(lua_State *L)
|
||||||
{
|
{
|
||||||
w__beginPass(L);
|
const std::vector<Canvas *> canvases = instance()->getCanvas();
|
||||||
return 0;
|
int n = 0;
|
||||||
|
|
||||||
|
for (Canvas *c : canvases)
|
||||||
|
{
|
||||||
|
luax_pushtype(L, c);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
n = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
|
static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd)
|
||||||
@@ -264,96 +249,6 @@ static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *
|
|||||||
delete ref;
|
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)
|
int w_captureScreenshot(lua_State *L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||||
@@ -1600,8 +1495,8 @@ int w_getStats(lua_State *L)
|
|||||||
lua_pushinteger(L, stats.drawCalls);
|
lua_pushinteger(L, stats.drawCalls);
|
||||||
lua_setfield(L, -2, "drawcalls");
|
lua_setfield(L, -2, "drawcalls");
|
||||||
|
|
||||||
lua_pushinteger(L, stats.renderPasses);
|
lua_pushinteger(L, stats.canvasSwitches);
|
||||||
lua_setfield(L, -2, "renderpasses");
|
lua_setfield(L, -2, "canvasswitches");
|
||||||
|
|
||||||
lua_pushinteger(L, stats.shaderSwitches);
|
lua_pushinteger(L, stats.shaderSwitches);
|
||||||
lua_setfield(L, -2, "shaderswitches");
|
lua_setfield(L, -2, "shaderswitches");
|
||||||
@@ -2142,6 +2037,8 @@ int w_inverseTransformPoint(lua_State *L)
|
|||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg functions[] =
|
||||||
{
|
{
|
||||||
{ "reset", w_reset },
|
{ "reset", w_reset },
|
||||||
|
{ "clear", w_clear },
|
||||||
|
{ "discard", w_discard },
|
||||||
{ "present", w_present },
|
{ "present", w_present },
|
||||||
|
|
||||||
{ "newImage", w_newImage },
|
{ "newImage", w_newImage },
|
||||||
@@ -2156,6 +2053,9 @@ static const luaL_Reg functions[] =
|
|||||||
{ "newText", w_newText },
|
{ "newText", w_newText },
|
||||||
{ "_newVideo", w_newVideo },
|
{ "_newVideo", w_newVideo },
|
||||||
|
|
||||||
|
{ "setCanvas", w_setCanvas },
|
||||||
|
{ "getCanvas", w_getCanvas },
|
||||||
|
|
||||||
{ "setColor", w_setColor },
|
{ "setColor", w_setColor },
|
||||||
{ "getColor", w_getColor },
|
{ "getColor", w_getColor },
|
||||||
{ "setBackgroundColor", w_setBackgroundColor },
|
{ "setBackgroundColor", w_setBackgroundColor },
|
||||||
@@ -2195,12 +2095,6 @@ static const luaL_Reg functions[] =
|
|||||||
{ "getSystemLimits", w_getSystemLimits },
|
{ "getSystemLimits", w_getSystemLimits },
|
||||||
{ "getStats", w_getStats },
|
{ "getStats", w_getStats },
|
||||||
|
|
||||||
{ "beginPass", w_beginPass },
|
|
||||||
{ "endPass", w_endPass },
|
|
||||||
{ "renderPass", w_renderPass },
|
|
||||||
{ "isPassActive", w_isPassActive },
|
|
||||||
{ "getPassCanvases", w_getPassCanvases },
|
|
||||||
|
|
||||||
{ "captureScreenshot", w_captureScreenshot },
|
{ "captureScreenshot", w_captureScreenshot },
|
||||||
|
|
||||||
{ "draw", w_draw },
|
{ "draw", w_draw },
|
||||||
@@ -2214,9 +2108,6 @@ static const luaL_Reg functions[] =
|
|||||||
{ "getWidth", w_getWidth },
|
{ "getWidth", w_getWidth },
|
||||||
{ "getHeight", w_getHeight },
|
{ "getHeight", w_getHeight },
|
||||||
{ "getDimensions", w_getDimensions },
|
{ "getDimensions", w_getDimensions },
|
||||||
{ "getPassWidth", w_getPassWidth },
|
|
||||||
{ "getPassHeight", w_getPassHeight },
|
|
||||||
{ "getPassDimensions", w_getPassDimensions },
|
|
||||||
|
|
||||||
{ "setScissor", w_setScissor },
|
{ "setScissor", w_setScissor },
|
||||||
{ "intersectScissor", w_intersectScissor },
|
{ "intersectScissor", w_intersectScissor },
|
||||||
|
|||||||
@@ -411,8 +411,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
|||||||
if (!graphics.get())
|
if (!graphics.get())
|
||||||
graphics.set(Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS));
|
graphics.set(Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS));
|
||||||
|
|
||||||
if (graphics.get() && graphics->isPassActive())
|
if (graphics.get() && graphics->isCanvasActive())
|
||||||
throw love::Exception("setMode cannot be called while a render pass is active in love.graphics.");
|
throw love::Exception("setMode cannot be called while a Canvas is active in love.graphics.");
|
||||||
|
|
||||||
WindowSettings f;
|
WindowSettings f;
|
||||||
|
|
||||||
@@ -627,8 +627,8 @@ void Window::close()
|
|||||||
{
|
{
|
||||||
if (graphics.get())
|
if (graphics.get())
|
||||||
{
|
{
|
||||||
if (graphics->isPassActive())
|
if (graphics->isCanvasActive())
|
||||||
throw love::Exception("close cannot be called while a render pass is active in love.graphics.");
|
throw love::Exception("close cannot be called while a Canvas is active in love.graphics.");
|
||||||
|
|
||||||
graphics->unSetMode();
|
graphics->unSetMode();
|
||||||
}
|
}
|
||||||
@@ -657,8 +657,8 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
|
|||||||
if (!window)
|
if (!window)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (graphics.get() && graphics->isPassActive())
|
if (graphics.get() && graphics->isCanvasActive())
|
||||||
throw love::Exception("setFullscreen cannot be called while a render pass is active in love.graphics.");
|
throw love::Exception("setFullscreen cannot be called while a Canvas is active in love.graphics.");
|
||||||
|
|
||||||
WindowSettings newsettings = settings;
|
WindowSettings newsettings = settings;
|
||||||
newsettings.fullscreen = fullscreen;
|
newsettings.fullscreen = fullscreen;
|
||||||
|
|||||||
@@ -547,12 +547,9 @@ function love.run()
|
|||||||
|
|
||||||
if love.graphics and love.graphics.isActive() then
|
if love.graphics and love.graphics.isActive() then
|
||||||
love.graphics.origin()
|
love.graphics.origin()
|
||||||
|
love.graphics.clear(love.graphics.getBackgroundColor())
|
||||||
|
|
||||||
if love.drawpasses then love.drawpasses() end
|
|
||||||
|
|
||||||
love.graphics.beginPass(love.graphics.getBackgroundColor())
|
|
||||||
if love.draw then love.draw() end
|
if love.draw then love.draw() end
|
||||||
love.graphics.endPass()
|
|
||||||
|
|
||||||
love.graphics.present()
|
love.graphics.present()
|
||||||
end
|
end
|
||||||
@@ -606,9 +603,6 @@ function love.errhand(msg)
|
|||||||
if love.audio then love.audio.stop() end
|
if love.audio then love.audio.stop() end
|
||||||
|
|
||||||
love.graphics.reset()
|
love.graphics.reset()
|
||||||
if love.graphics.isPassActive() then
|
|
||||||
love.graphics.endPass()
|
|
||||||
end
|
|
||||||
|
|
||||||
local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14)))
|
local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14)))
|
||||||
|
|
||||||
@@ -636,8 +630,10 @@ function love.errhand(msg)
|
|||||||
p = string.gsub(p, "%[string \"(.-)\"%]", "%1")
|
p = string.gsub(p, "%[string \"(.-)\"%]", "%1")
|
||||||
|
|
||||||
local function draw()
|
local function draw()
|
||||||
|
love.graphics.clear(89/255, 157/255, 220/255)
|
||||||
local pos = love.window.toPixels(70)
|
local pos = love.window.toPixels(70)
|
||||||
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
|
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
|
||||||
|
love.graphics.present()
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
@@ -659,8 +655,7 @@ function love.errhand(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.renderPass(89/255, 157/255, 220/255, draw)
|
draw()
|
||||||
love.graphics.present()
|
|
||||||
|
|
||||||
if love.timer then
|
if love.timer then
|
||||||
love.timer.sleep(0.1)
|
love.timer.sleep(0.1)
|
||||||
|
|||||||
+10
-21
@@ -1010,17 +1010,12 @@ const unsigned char boot_lua[] =
|
|||||||
0x2e, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
0x2e, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f,
|
||||||
0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a,
|
0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x70, 0x61, 0x73,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63,
|
||||||
0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77,
|
0x6c, 0x65, 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
|
0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x62,
|
0x72, 0x28, 0x29, 0x29, 0x0a,
|
||||||
0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70,
|
|
||||||
0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64,
|
|
||||||
0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x29, 0x29, 0x0a,
|
|
||||||
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x20, 0x74, 0x68,
|
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x20, 0x74, 0x68,
|
||||||
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
|
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65,
|
|
||||||
0x6e, 0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a,
|
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70,
|
||||||
0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
|
0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
|
||||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
@@ -1106,12 +1101,6 @@ const unsigned char boot_lua[] =
|
|||||||
0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
|
0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65, 0x73,
|
0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65, 0x73,
|
||||||
0x65, 0x74, 0x28, 0x29, 0x0a,
|
0x65, 0x74, 0x28, 0x29, 0x0a,
|
||||||
0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e,
|
|
||||||
0x69, 0x73, 0x50, 0x61, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65,
|
|
||||||
0x6e, 0x0a,
|
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65, 0x6e,
|
|
||||||
0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a,
|
|
||||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
|
||||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
|
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
|
||||||
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4e, 0x65, 0x77, 0x46, 0x6f,
|
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4e, 0x65, 0x77, 0x46, 0x6f,
|
||||||
0x6e, 0x74, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65,
|
0x6e, 0x74, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65,
|
||||||
@@ -1151,6 +1140,9 @@ const unsigned char boot_lua[] =
|
|||||||
0x5c, 0x22, 0x25, 0x5d, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x22, 0x29, 0x0a,
|
0x5c, 0x22, 0x25, 0x5d, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x22, 0x29, 0x0a,
|
||||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x72,
|
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x72,
|
||||||
0x61, 0x77, 0x28, 0x29, 0x0a,
|
0x61, 0x77, 0x28, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x6c,
|
||||||
|
0x65, 0x61, 0x72, 0x28, 0x38, 0x39, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2f, 0x32, 0x35,
|
||||||
|
0x35, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x2f, 0x32, 0x35, 0x35, 0x29, 0x0a,
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
|
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65,
|
||||||
0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x37,
|
0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x37,
|
||||||
0x30, 0x29, 0x0a,
|
0x30, 0x29, 0x0a,
|
||||||
@@ -1158,6 +1150,8 @@ const unsigned char boot_lua[] =
|
|||||||
0x69, 0x6e, 0x74, 0x66, 0x28, 0x70, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20,
|
0x69, 0x6e, 0x74, 0x66, 0x28, 0x70, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20,
|
||||||
0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57,
|
0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57,
|
||||||
0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x20, 0x2d, 0x20, 0x70, 0x6f, 0x73, 0x29, 0x0a,
|
0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x20, 0x2d, 0x20, 0x70, 0x6f, 0x73, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
|
||||||
|
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
|
||||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a,
|
0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a,
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28,
|
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28,
|
||||||
@@ -1194,12 +1188,7 @@ const unsigned char boot_lua[] =
|
|||||||
0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65,
|
0x09, 0x09, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a,
|
||||||
0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x28, 0x38, 0x39, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31,
|
|
||||||
0x35, 0x37, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x64,
|
|
||||||
0x72, 0x61, 0x77, 0x29, 0x0a,
|
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
|
|
||||||
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a,
|
|
||||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68,
|
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68,
|
||||||
0x65, 0x6e, 0x0a,
|
0x65, 0x6e, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65,
|
||||||
|
|||||||
Reference in New Issue
Block a user