diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index a8a8c5e68..d403e8b02 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -194,5 +194,19 @@ void Matrix::transform(Vertex *dst, const Vertex *src, int size) const } } +Matrix Matrix::ortho(float left, float right, float bottom, float top) +{ + Matrix m; + + m.e[0] = 2.0f / (right - left); + m.e[5] = 2.0f / (top - bottom); + m.e[10] = -1.0; + + m.e[12] = -(right + left) / (right - left); + m.e[13] = -(top + bottom) / (top - bottom); + + return m; +} + } // love diff --git a/src/common/Matrix.h b/src/common/Matrix.h index 2a0091448..0e33b37d9 100644 --- a/src/common/Matrix.h +++ b/src/common/Matrix.h @@ -150,6 +150,12 @@ public: **/ void transform(Vertex *dst, const Vertex *src, int size) const; + /** + * Creates a new orthographic projection matrix with depth in the range of + * [-1, 1]. + **/ + static Matrix ortho(float left, float right, float bottom, float top); + private: /** diff --git a/src/common/Object.cpp b/src/common/Object.cpp index 577f7dfa2..dc7df2306 100644 --- a/src/common/Object.cpp +++ b/src/common/Object.cpp @@ -21,6 +21,8 @@ // LOVE #include "Object.h" +#include + namespace love { diff --git a/src/common/Object.h b/src/common/Object.h index 348da1b63..791c1dac1 100644 --- a/src/common/Object.h +++ b/src/common/Object.h @@ -92,11 +92,73 @@ public: }; // AutoRelease + /** + * Partial re-implementation + specialization of std::shared_ptr. We can't + * use C++11's stdlib yet... + **/ + template + class StrongRef + { + public: + + StrongRef() + : object(nullptr) + { + } + + StrongRef(T *obj) + : object(obj) + { + if (object) object->retain(); + } + + StrongRef(const StrongRef &other) + : object(other.get()) + { + if (object) object->retain(); + } + + ~StrongRef() + { + if (object) object->release(); + } + + StrongRef &operator = (const StrongRef &other) + { + set(other.get()); + return *this; + } + + T *operator->() const + { + return object; + } + + void set(T *obj) + { + if (obj) obj->retain(); + if (object) object->release(); + object = obj; + } + + T *get() const + { + return object; + } + + private: + + T *object; + + }; // StrongRef + private: // The reference count. int count; + }; // Object + } // love #endif // LOVE_OBJECT_H diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 9d845a89e..7786f1c37 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -102,7 +102,6 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder) , decoder(decoder) , toLoop(0) { - decoder->retain(); alGenBuffers(MAX_BUFFERS, streamBuffers); float z[3] = {0, 0, 0}; @@ -136,13 +135,15 @@ Source::Source(const Source &s) { if (type == TYPE_STREAM) { - if (s.decoder) - decoder = s.decoder->clone(); + if (s.decoder.get()) + { + love::sound::Decoder *dec = s.decoder->clone(); + decoder.set(dec); + dec->release(); + } alGenBuffers(MAX_BUFFERS, streamBuffers); } - else - staticBuffer->retain(); setFloatv(position, s.position); setFloatv(velocity, s.velocity); @@ -156,12 +157,6 @@ Source::~Source() if (type == TYPE_STREAM) alDeleteBuffers(MAX_BUFFERS, streamBuffers); - - if (staticBuffer) - staticBuffer->release(); - - if (decoder) - decoder->release(); } love::audio::Source *Source::clone() @@ -273,7 +268,7 @@ bool Source::update() offsetSamples += (curOffsetSamples - newOffsetSamples); offsetSeconds += (curOffsetSecs - newOffsetSecs); - streamAtomic(buffer, decoder); + streamAtomic(buffer, decoder.get()); alSourceQueueBuffers(source, 1, &buffer); } return true; @@ -544,7 +539,7 @@ bool Source::playAtomic() for (unsigned int i = 0; i < MAX_BUFFERS; i++) { - streamAtomic(streamBuffers[i], decoder); + streamAtomic(streamBuffers[i], decoder.get()); ++usedBuffers; if (decoder->isFinished()) break; diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index a2cc9520c..33342be8b 100644 --- a/src/modules/audio/openal/Source.h +++ b/src/modules/audio/openal/Source.h @@ -147,7 +147,7 @@ private: static const unsigned int MAX_BUFFERS = 32; ALuint streamBuffers[MAX_BUFFERS]; - StaticDataBuffer *staticBuffer; + Object::StrongRef staticBuffer; float pitch; float volume; @@ -181,7 +181,7 @@ private: int channels; - love::sound::Decoder *decoder; + Object::StrongRef decoder; unsigned int toLoop; diff --git a/src/modules/filesystem/physfs/File.cpp b/src/modules/filesystem/physfs/File.cpp index 95eafa800..be137c4b0 100644 --- a/src/modules/filesystem/physfs/File.cpp +++ b/src/modules/filesystem/physfs/File.cpp @@ -69,23 +69,36 @@ bool File::open(Mode mode) if (file != 0) return false; - this->mode = mode; + PHYSFS_getLastError(); // Clear the error buffer. + PHYSFS_File *handle = nullptr; switch (mode) { case READ: - file = PHYSFS_openRead(filename.c_str()); + handle = PHYSFS_openRead(filename.c_str()); break; case APPEND: - file = PHYSFS_openAppend(filename.c_str()); + handle = PHYSFS_openAppend(filename.c_str()); break; case WRITE: - file = PHYSFS_openWrite(filename.c_str()); + handle = PHYSFS_openWrite(filename.c_str()); break; default: break; } + if (handle == nullptr) + { + const char *err = PHYSFS_getLastError(); + if (err == nullptr) + err = "unknown error"; + throw love::Exception("Could not open file %s (%s)", filename.c_str(), err); + } + + file = handle; + + this->mode = mode; + if (file != 0 && !setBuffer(bufferMode, bufferSize)) { // Revert to buffer defaults if we don't successfully set the buffer. diff --git a/src/modules/font/ImageRasterizer.cpp b/src/modules/font/ImageRasterizer.cpp index fd0ec21b2..f7d1fada5 100644 --- a/src/modules/font/ImageRasterizer.cpp +++ b/src/modules/font/ImageRasterizer.cpp @@ -39,13 +39,11 @@ ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, i , glyphs(glyphs) , numglyphs(numglyphs) { - imageData->retain(); load(); } ImageRasterizer::~ImageRasterizer() { - imageData->release(); } int ImageRasterizer::getLineHeight() const diff --git a/src/modules/font/ImageRasterizer.h b/src/modules/font/ImageRasterizer.h index 482d442c6..59a16205e 100644 --- a/src/modules/font/ImageRasterizer.h +++ b/src/modules/font/ImageRasterizer.h @@ -53,7 +53,7 @@ private: void load(); // The image data - love::image::ImageData *imageData; + Object::StrongRef imageData; // The glyphs in the font uint32 *glyphs; diff --git a/src/modules/font/freetype/TrueTypeRasterizer.cpp b/src/modules/font/freetype/TrueTypeRasterizer.cpp index 3cdfac6c3..0cf92f873 100644 --- a/src/modules/font/freetype/TrueTypeRasterizer.cpp +++ b/src/modules/font/freetype/TrueTypeRasterizer.cpp @@ -51,14 +51,11 @@ TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size) metrics.ascent = s.ascender >> 6; metrics.descent = s.descender >> 6; metrics.height = s.height >> 6; - - data->retain(); } TrueTypeRasterizer::~TrueTypeRasterizer() { FT_Done_Face(face); - data->release(); } int TrueTypeRasterizer::getLineHeight() const diff --git a/src/modules/font/freetype/TrueTypeRasterizer.h b/src/modules/font/freetype/TrueTypeRasterizer.h index 5600f324c..6ddaf0461 100644 --- a/src/modules/font/freetype/TrueTypeRasterizer.h +++ b/src/modules/font/freetype/TrueTypeRasterizer.h @@ -58,7 +58,7 @@ private: FT_Face face; // File data - Data *data; + Object::StrongRef data; }; // FreetypeRasterizer } // freetype diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index c8c68ba80..36f01279c 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -99,6 +99,16 @@ bool Graphics::getConstant(SystemLimit in, const char *&out) return systemLimits.find(in, out); } +bool Graphics::getConstant(const char *in, StackType &out) +{ + return stackTypes.find(in, out); +} + +bool Graphics::getConstant(StackType in, const char *&out) +{ + return stackTypes.find(in, out); +} + StringMap::Entry Graphics::drawModeEntries[] = { { "line", Graphics::DRAW_LINE }, @@ -165,5 +175,13 @@ StringMap::Entry Graphics::syst StringMap Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries)); +StringMap::Entry Graphics::stackTypeEntries[] = +{ + {"all", Graphics::STACK_ALL}, + {"transform", Graphics::STACK_TRANSFORM}, +}; + +StringMap Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries)); + } // graphics } // love diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index c73b938bc..300d28035 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -96,6 +96,13 @@ public: LIMIT_MAX_ENUM }; + enum StackType + { + STACK_ALL, + STACK_TRANSFORM, + STACK_MAX_ENUM + }; + struct RendererInfo { std::string name; @@ -148,6 +155,9 @@ public: static bool getConstant(const char *in, SystemLimit &out); static bool getConstant(SystemLimit in, const char *&out); + static bool getConstant(const char *in, StackType &out); + static bool getConstant(StackType in, const char *&out); + private: static StringMap::Entry drawModeEntries[]; @@ -171,6 +181,9 @@ private: static StringMap::Entry systemLimitEntries[]; static StringMap systemLimits; + static StringMap::Entry stackTypeEntries[]; + static StringMap stackTypes; + }; // Graphics } // graphics diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 818652487..8cfae960f 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -588,16 +588,13 @@ void Canvas::unloadVolatile() fbo = depth_stencil = texture = 0; resolve_fbo = msaa_buffer = 0; - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); - attachedCanvases.clear(); } void Canvas::drawv(const Matrix &t, const Vertex *v) { - glPushMatrix(); - glMultMatrixf((const GLfloat *)t.getElements()); + OpenGL::TempTransform transform(gl); + transform.get() *= t; predraw(); @@ -614,8 +611,6 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) glDisableClientState(GL_VERTEX_ARRAY); postdraw(); - - glPopMatrix(); } void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) @@ -688,16 +683,8 @@ void Canvas::setupGrab() strategy->bindFBO(fbo); gl.setViewport(OpenGL::Viewport(0, 0, width, height)); - // Reset the projection matrix - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - // Set up orthographic view (no depth) - glOrtho(0.0, width, 0.0, height, -1.0, 1.0); - - // Switch back to modelview matrix - glMatrixMode(GL_MODELVIEW); + // Set up the projection matrix + gl.matrices.projection.push_back(Matrix::ortho(0.0, width, 0.0, height)); // Make sure the correct sRGB setting is used when drawing to the canvas. if (format == FORMAT_SRGB) @@ -751,11 +738,8 @@ void Canvas::startGrab(const std::vector &canvases) // Attach the canvas textures to the active FBO and set up MRTs. strategy->setAttachments(canvases); - for (size_t i = 0; i < canvases.size(); i++) - canvases[i]->retain(); - - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); + // We want to avoid reference cycles, so we don't retain the attached + // Canvases here. The code in Graphics::setCanvas retains them. attachedCanvases = canvases; } @@ -770,10 +754,6 @@ void Canvas::startGrab() // make sure the FBO is only using a single canvas strategy->setAttachments(); - // release any previously attached canvases - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); - attachedCanvases.clear(); } @@ -783,9 +763,7 @@ void Canvas::stopGrab(bool switchingToOtherCanvas) if (current != this) return; - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + gl.matrices.projection.pop_back(); if (switchingToOtherCanvas) { @@ -1120,12 +1098,6 @@ bool Canvas::isFormatSupported(Canvas::Format format) return supported; } -void Canvas::bindDefaultCanvas() -{ - if (current != nullptr) - current->stopGrab(); -} - bool Canvas::getConstant(const char *in, Format &out) { return formats.find(in, out); diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index d12d5ed42..a0fd6f1a1 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -120,7 +120,6 @@ public: static bool isFormatSupported(Format format); static Canvas *current; - static void bindDefaultCanvas(); // The viewport dimensions of the system (default) framebuffer. static OpenGL::Viewport systemViewport; diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index df51c302d..a0a62571d 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -87,13 +87,10 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter) } delete gd; - - rasterizer->retain(); } Font::~Font() { - rasterizer->release(); unloadVolatile(); } @@ -368,11 +365,11 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing, // second (using the struct's < operator). std::sort(glyphinfolist.begin(), glyphinfolist.end()); - glPushMatrix(); - Matrix t; t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -393,8 +390,6 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing, glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - - glPopMatrix(); } int Font::getWidth(const std::string &str) diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index 403b0ef45..2ed3b931a 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -183,7 +183,7 @@ private: Glyph *addGlyph(uint32 glyph); Glyph *findGlyph(uint32 glyph); - love::font::Rasterizer *rasterizer; + Object::StrongRef rasterizer; int height; float lineHeight; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c560c1a45..082d0478c 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -44,20 +44,15 @@ namespace opengl { Graphics::Graphics() - : currentFont(0) - , lineStyle(LINE_SMOOTH) - , lineJoin(LINE_JOIN_MITER) - , lineWidth(1) - , matrixLimit(0) - , userMatrices(0) - , colorMask() - , width(0) + : width(0) , height(0) , created(false) , activeStencil(false) - , savedState() , displayedMinReqWarning(false) { + states.reserve(10); + states.push_back(DisplayState()); + currentWindow = love::window::sdl::Window::createSingleton(); int w, h; @@ -71,8 +66,8 @@ Graphics::Graphics() Graphics::~Graphics() { - if (currentFont != 0) - currentFont->release(); + // We do this manually so the love objects get released before the window. + states.clear(); currentWindow->release(); } @@ -82,50 +77,86 @@ const char *Graphics::getName() const return "love.graphics.opengl"; } -DisplayState Graphics::saveState() -{ - DisplayState s; - - s.color = getColor(); - s.backgroundColor = getBackgroundColor(); - - s.blendMode = getBlendMode(); - //get line style - s.lineStyle = getLineStyle(); - s.lineJoin = getLineJoin(); - //get the point size - glGetFloatv(GL_POINT_SIZE, &s.pointSize); - //get scissor status - s.scissor = (glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE); - //do we have scissor, if so, store the box - if (s.scissor) - s.scissorBox = gl.getScissor(); - - for (int i = 0; i < 4; i++) - s.colorMask[i] = colorMask[i]; - - wireframe = isWireframe(); - - return s; -} - void Graphics::restoreState(const DisplayState &s) { setColor(s.color); setBackgroundColor(s.backgroundColor); + setBlendMode(s.blendMode); - setLineWidth(lineWidth); + + setLineWidth(s.lineWidth); setLineStyle(s.lineStyle); setLineJoin(s.lineJoin); + setPointSize(s.pointSize); + if (s.scissor) setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); else setScissor(); - setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]); + + setFont(s.font.get()); + setShader(s.shader.get()); + setCanvas(s.canvases); + + setColorMask(s.colorMask); setWireframe(s.wireframe); } +void Graphics::restoreStateChecked(const DisplayState &s) +{ + const DisplayState &cur = states.back(); + + if (*(uint32 *) &s.color.r != *(uint32 *) &cur.color.r) + setColor(s.color); + + if (*(uint32 *) &s.backgroundColor.r != *(uint32 *) &cur.backgroundColor.r) + setBackgroundColor(s.backgroundColor); + + if (s.blendMode != cur.blendMode) + setBlendMode(s.blendMode); + + // These are just simple assignments. + setLineWidth(s.lineWidth); + setLineStyle(s.lineStyle); + setLineJoin(s.lineJoin); + + if (s.pointSize != cur.pointSize) + setPointSize(s.pointSize); + + if (s.scissor != cur.scissor || (s.scissor && !(s.scissorBox == cur.scissorBox))) + { + if (s.scissor) + setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); + else + setScissor(); + } + + setFont(s.font.get()); + setShader(s.shader.get()); + + for (size_t i = 0; i < s.canvases.size() && i < cur.canvases.size(); i++) + { + if (s.canvases[i].get() != cur.canvases[i].get()) + { + setCanvas(s.canvases); + break; + } + } + + for (int i = 0; i < 4; i++) + { + if (s.colorMask[i] != cur.colorMask[i]) + { + setColorMask(s.colorMask); + break; + } + } + + if (s.wireframe != cur.wireframe) + setWireframe(s.wireframe); +} + void Graphics::setViewportSize(int width, int height) { this->width = width; @@ -136,8 +167,8 @@ void Graphics::setViewportSize(int width, int height) // We want to affect the main screen, not any Canvas that's currently active // (not that any *should* be active when this is called.) - Canvas *c = Canvas::current; - Canvas::bindDefaultCanvas(); + std::vector canvases = getCanvas(); + setCanvas(); // Set the viewport to top-left corner. gl.setViewport(OpenGL::Viewport(0, 0, width, height)); @@ -146,18 +177,11 @@ void Graphics::setViewportSize(int width, int height) // made aware of the new system viewport size. Canvas::systemViewport = gl.getViewport(); - // Reset the projection matrix - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // Set up orthographic view (no depth) - glOrtho(0.0, width, height, 0.0, -1.0, 1.0); - - glMatrixMode(GL_MODELVIEW); + // Set up the projection matrix + gl.matrices.projection.back() = Matrix::ortho(0.0, width, height, 0.0); // Restore the previously active Canvas. - if (c != nullptr) - c->startGrab(c->getAttachedCanvases()); + setCanvas(canvases); } bool Graphics::setMode(int width, int height, bool &sRGB) @@ -202,7 +226,8 @@ bool Graphics::setMode(int width, int height, bool &sRGB) glEnable(GL_BLEND); // Enable all color component writes. - setColorMask(true, true, true, true); + bool colormask[] = {true, true, true, true}; + setColorMask(colormask); // Auto-generated mipmaps should be the best quality possible glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); @@ -211,28 +236,9 @@ bool Graphics::setMode(int width, int height, bool &sRGB) glEnable(GL_TEXTURE_2D); gl.setTextureUnit(0); - // Reset modelview matrix - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - // Set pixel row alignment glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Reload all volatile objects. - if (!Volatile::loadAll()) - std::cerr << "Could not reload all volatile objects." << std::endl; - - // Restore the display state. - restoreState(savedState); - pixel_size_stack.clear(); - pixel_size_stack.reserve(5); - pixel_size_stack.push_back(1); - - // Get the maximum number of matrices - // subtract a few to give the engine some room. - glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit); - matrixLimit -= 5; - // Set whether drawing converts input from linear -> sRGB colorspace. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) { @@ -258,6 +264,17 @@ bool Graphics::setMode(int width, int height, bool &sRGB) setDebug(enabledebug); + // Reload all volatile objects. + if (!Volatile::loadAll()) + std::cerr << "Could not reload all volatile objects." << std::endl; + + // Restore the graphics state. + restoreState(states.back()); + + pixel_size_stack.clear(); + pixel_size_stack.reserve(5); + pixel_size_stack.push_back(1); + return true; } @@ -266,9 +283,6 @@ void Graphics::unSetMode() if (!isCreated()) return; - // Window re-creation may destroy the GL context, so we must save the state. - savedState = saveState(); - // Unload all volatile objects. These must be reloaded after the display // mode change. Volatile::unloadAll(); @@ -337,8 +351,6 @@ void Graphics::reset() { DisplayState s; discardStencil(); - Canvas::bindDefaultCanvas(); - Shader::detach(); origin(); restoreState(s); } @@ -370,13 +382,18 @@ bool Graphics::isCreated() const void Graphics::setScissor(int x, int y, int width, int height) { + OpenGL::Viewport box(x, y, width, height); + + states.back().scissor = true; glEnable(GL_SCISSOR_TEST); // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor. - gl.setScissor(OpenGL::Viewport(x, y, width, height)); + gl.setScissor(box); + states.back().scissorBox = box; } void Graphics::setScissor() { + states.back().scissor = false; glDisable(GL_SCISSOR_TEST); } @@ -389,7 +406,7 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const width = scissor.w; height = scissor.h; - return glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE; + return states.back().scissor; } void Graphics::defineStencil() @@ -413,7 +430,7 @@ void Graphics::useStencil(bool invert) { glStencilFunc(GL_EQUAL, (GLint)(!invert), 1); // invert ? 0 : 1 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + setColorMask(states.back().colorMask); } void Graphics::discardStencil() @@ -421,7 +438,7 @@ void Graphics::discardStencil() if (!activeStencil) return; - setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + setColorMask(states.back().colorMask); glDisable(GL_STENCIL_TEST); activeStencil = false; } @@ -580,119 +597,186 @@ Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode) void Graphics::setColor(const Color &c) { gl.setColor(c); + states.back().color = c; } Color Graphics::getColor() const { - return gl.getColor(); + return states.back().color; } void Graphics::setBackgroundColor(const Color &c) { gl.setClearColor(c); + states.back().backgroundColor = c; } Color Graphics::getBackgroundColor() const { - return gl.getClearColor(); + return states.back().backgroundColor; } void Graphics::setFont(Font *font) { - Object::AutoRelease fontrelease(currentFont); - - currentFont = font; - - if (font != 0) - currentFont->retain(); + DisplayState &state = states.back(); + state.font.set(font); } Font *Graphics::getFont() const { - return currentFont; + return states.back().font.get(); } -void Graphics::setColorMask(bool r, bool g, bool b, bool a) +void Graphics::setShader(Shader *shader) { - colorMask[0] = r; - colorMask[1] = g; - colorMask[2] = b; - colorMask[3] = a; + if (shader == nullptr) + return setShader(); - glColorMask((GLboolean) r, (GLboolean) g, (GLboolean) b, (GLboolean) a); + DisplayState &state = states.back(); + + shader->attach(); + + state.shader.set(shader); +} + +void Graphics::setShader() +{ + DisplayState &state = states.back(); + + Shader::detach(); + + state.shader.set(nullptr); +} + +Shader *Graphics::getShader() const +{ + return states.back().shader.get(); +} + +void Graphics::setCanvas(Canvas *canvas) +{ + if (canvas == nullptr) + return setCanvas(); + + DisplayState &state = states.back(); + + canvas->startGrab(); + + std::vector> canvasref; + canvasref.push_back(canvas); + + std::swap(state.canvases, canvasref); +} + +void Graphics::setCanvas(const std::vector &canvases) +{ + if (canvases.size() == 0) + return setCanvas(); + else if (canvases.size() == 1) + return setCanvas(canvases[0]); + + DisplayState &state = states.back(); + + auto attachments = std::vector(canvases.begin() + 1, canvases.end()); + canvases[0]->startGrab(attachments); + + std::vector> canvasrefs; + canvasrefs.reserve(canvases.size()); + + for (Canvas *c : canvases) + canvasrefs.push_back(c); + + std::swap(state.canvases, canvasrefs); +} + +void Graphics::setCanvas(const std::vector> &canvases) +{ + std::vector canvaslist; + canvaslist.reserve(canvases.size()); + + for (const Object::StrongRef &c : canvases) + canvaslist.push_back(c.get()); + + return setCanvas(canvaslist); +} + +void Graphics::setCanvas() +{ + DisplayState &state = states.back(); + + if (Canvas::current != nullptr) + Canvas::current->stopGrab(); + + state.canvases.clear(); +} + +std::vector Graphics::getCanvas() const +{ + std::vector canvases; + canvases.reserve(states.back().canvases.size()); + + for (const Object::StrongRef &c : states.back().canvases) + canvases.push_back(c.get()); + + return canvases; +} + +void Graphics::setColorMask(const bool mask[4]) +{ + for (int i = 0; i < 4; i++) + states.back().colorMask[i] = mask[i]; + + glColorMask(mask[0], mask[1], mask[2], mask[3]); } const bool *Graphics::getColorMask() const { - return colorMask; + return states.back().colorMask; } void Graphics::setBlendMode(Graphics::BlendMode mode) { - OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; + OpenGL::BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; switch (mode) { case BLEND_ALPHA: - state.srcRGB = GL_SRC_ALPHA; - state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; + blend.srcRGB = GL_SRC_ALPHA; + blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_ALPHA; break; case BLEND_MULTIPLY: - state.srcRGB = state.srcA = GL_DST_COLOR; - state.dstRGB = state.dstA = GL_ZERO; + blend.srcRGB = blend.srcA = GL_DST_COLOR; + blend.dstRGB = blend.dstA = GL_ZERO; break; case BLEND_PREMULTIPLIED: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_ALPHA; break; case BLEND_SUBTRACT: - state.func = GL_FUNC_REVERSE_SUBTRACT; + blend.func = GL_FUNC_REVERSE_SUBTRACT; case BLEND_ADD: - state.srcRGB = state.srcA = GL_SRC_ALPHA; - state.dstRGB = state.dstA = GL_ONE; + blend.srcRGB = blend.srcA = GL_SRC_ALPHA; + blend.dstRGB = blend.dstA = GL_ONE; break; case BLEND_SCREEN: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_COLOR; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_COLOR; break; case BLEND_REPLACE: default: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ZERO; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ZERO; break; } - gl.setBlendState(state); + gl.setBlendState(blend); + states.back().blendMode = mode; } Graphics::BlendMode Graphics::getBlendMode() const { - OpenGL::BlendState state = gl.getBlendState(); - - if (state.func == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE - return BLEND_SUBTRACT; - // Everything else has equation == GL_FUNC_ADD. - else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA) - { - if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE) - return BLEND_ADD; - else if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_ALPHA; // alpha blend mode fallback for very old OpenGL versions. - else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO) - return BLEND_MULTIPLY; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_PREMULTIPLIED; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_COLOR) - return BLEND_SCREEN; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO) - return BLEND_REPLACE; - } - else if (state.srcRGB == GL_SRC_ALPHA && state.srcA == GL_ONE && - state.dstRGB == GL_ONE_MINUS_SRC_ALPHA && state.dstA == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_ALPHA; - - throw Exception("Unknown blend mode"); + return states.back().blendMode; } void Graphics::setDefaultFilter(const Texture::Filter &f) @@ -719,66 +803,69 @@ void Graphics::getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpn void Graphics::setLineWidth(float width) { - lineWidth = width; + states.back().lineWidth = width; } void Graphics::setLineStyle(Graphics::LineStyle style) { - lineStyle = style; + states.back().lineStyle = style; } void Graphics::setLineJoin(Graphics::LineJoin join) { - lineJoin = join; + states.back().lineJoin = join; } float Graphics::getLineWidth() const { - return lineWidth; + return states.back().lineWidth; } Graphics::LineStyle Graphics::getLineStyle() const { - return lineStyle; + return states.back().lineStyle; } Graphics::LineJoin Graphics::getLineJoin() const { - return lineJoin; + return states.back().lineJoin; } void Graphics::setPointSize(float size) { - glPointSize((GLfloat)size); + glPointSize(size); + states.back().pointSize = size; } float Graphics::getPointSize() const { - GLfloat size; - glGetFloatv(GL_POINT_SIZE, &size); - return (float)size; + return states.back().pointSize; } void Graphics::setWireframe(bool enable) { - wireframe = enable; glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL); + states.back().wireframe = enable; } bool Graphics::isWireframe() const { - return wireframe; + return states.back().wireframe; } void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (currentFont != nullptr) - currentFont->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky); + DisplayState &state = states.back(); + + if (state.font.get() != nullptr) + state.font->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky); } void Graphics::printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (currentFont == nullptr) + DisplayState &state = states.back(); + + if (state.font.get() == nullptr) return; if (wrap < 0.0f) @@ -790,59 +877,49 @@ void Graphics::printf(const std::string &str, float x, float y, float wrap, Alig // wrappedlines indicates which lines were automatically wrapped. It's // guaranteed to have the same number of elements as lines_to_draw. vector wrappedlines; - vector lines_to_draw = currentFont->getWrap(str, wrap, 0, &wrappedlines); - - glPushMatrix(); + vector lines_to_draw = state.font->getWrap(str, wrap, 0, &wrappedlines); static Matrix t; t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; x = y = 0.0f; - try - { - // now for the actual printing - vector::const_iterator line_iter, line_end = lines_to_draw.end(); - float extra_spacing = 0.0f; - int num_spaces = 0; - int i = 0; + // now for the actual printing + vector::const_iterator line_iter, line_end = lines_to_draw.end(); + float extra_spacing = 0.0f; + int num_spaces = 0; + int i = 0; - for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter) + for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter) + { + float width = static_cast(state.font->getWidth(*line_iter)); + switch (align) { - float width = static_cast(currentFont->getWidth(*line_iter)); - switch (align) - { - case ALIGN_RIGHT: - currentFont->print(*line_iter, ceilf(x + (wrap - width)), ceilf(y), 0.0f); - break; - case ALIGN_CENTER: - currentFont->print(*line_iter, ceilf(x + (wrap - width) / 2), ceilf(y), 0.0f); - break; - case ALIGN_JUSTIFY: - num_spaces = std::count(line_iter->begin(), line_iter->end(), ' '); - if (wrappedlines[i] && num_spaces >= 1) - extra_spacing = (wrap - width) / float(num_spaces); - else - extra_spacing = 0.0f; - currentFont->print(*line_iter, ceilf(x), ceilf(y), extra_spacing); - break; - case ALIGN_LEFT: - default: - currentFont->print(*line_iter, ceilf(x), ceilf(y), 0.0f); - break; - } - y += currentFont->getHeight() * currentFont->getLineHeight(); - i++; + case ALIGN_RIGHT: + state.font->print(*line_iter, ceilf(x + (wrap - width)), ceilf(y), 0.0f); + break; + case ALIGN_CENTER: + state.font->print(*line_iter, ceilf(x + (wrap - width) / 2), ceilf(y), 0.0f); + break; + case ALIGN_JUSTIFY: + num_spaces = std::count(line_iter->begin(), line_iter->end(), ' '); + if (wrappedlines[i] && num_spaces >= 1) + extra_spacing = (wrap - width) / float(num_spaces); + else + extra_spacing = 0.0f; + state.font->print(*line_iter, ceilf(x), ceilf(y), extra_spacing); + break; + case ALIGN_LEFT: + default: + state.font->print(*line_iter, ceilf(x), ceilf(y), 0.0f); + break; } + y += state.font->getHeight() * state.font->getLineHeight(); + i++; } - catch (love::Exception &) - { - glPopMatrix(); - throw; - } - - glPopMatrix(); } /** @@ -860,22 +937,24 @@ void Graphics::point(float x, float y) void Graphics::polyline(const float *coords, size_t count) { - if (lineJoin == LINE_JOIN_NONE) + DisplayState &state = states.back(); + + if (state.lineJoin == LINE_JOIN_NONE) { NoneJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } - else if (lineJoin == LINE_JOIN_BEVEL) + else if (state.lineJoin == LINE_JOIN_BEVEL) { BevelJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } else // LINE_JOIN_MITER { MiterJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } } @@ -982,9 +1061,8 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool { // Temporarily unbind the currently active canvas (glReadPixels reads the // active framebuffer, not the main one.) - Canvas *curcanvas = Canvas::current; - if (curcanvas) - Canvas::bindDefaultCanvas(); + std::vector canvases = getCanvas(); + setCanvas(); int w = getWidth(); int h = getHeight(); @@ -1005,10 +1083,7 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool { delete[] pixels; delete[] screenshot; - - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); - + setCanvas(canvases); throw love::Exception("Out of memory."); } @@ -1040,16 +1115,12 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool catch (love::Exception &) { delete[] screenshot; - - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); - + setCanvas(canvases); throw; } // Re-bind the active canvas, if necessary. - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); + setCanvas(canvases); return img; } @@ -1130,53 +1201,140 @@ bool Graphics::isSupported(Support feature) const } } -void Graphics::push() +void Graphics::push(StackType type) { - if (userMatrices == matrixLimit) - throw Exception("Maximum stack depth reached. (More pushes than pops?)"); - glPushMatrix(); - ++userMatrices; + if (stackTypes.size() == MAX_USER_STACK_DEPTH) + throw Exception("Maximum stack depth reached (more pushes than pops?)"); + + gl.pushTransform(); + pixel_size_stack.push_back(pixel_size_stack.back()); + + if (type == STACK_ALL) + states.push_back(states.back()); + + stackTypes.push_back(type); } void Graphics::pop() { - if (userMatrices < 1) - throw Exception("Minimum stack depth reached. (More pops than pushes?)"); - glPopMatrix(); - --userMatrices; + if (stackTypes.size() < 1) + throw Exception("Minimum stack depth reached (more pops than pushes?)"); + + gl.popTransform(); pixel_size_stack.pop_back(); + + if (stackTypes.back() == STACK_ALL) + { + DisplayState &newstate = states[states.size() - 2]; + + // Hack: the Lua-facing love.graphics.print function will set the current + // font if needed, but only on its first call... we always want a font. + if (newstate.font.get() == nullptr) + newstate.font.set(states.back().font.get()); + + restoreStateChecked(newstate); + + // The last two states in the stack should be equal now. + states.pop_back(); + } + + stackTypes.pop_back(); } void Graphics::rotate(float r) { - glRotatef(LOVE_TODEG(r), 0, 0, 1); + gl.getTransform().rotate(r); } void Graphics::scale(float x, float y) { - glScalef(x, y, 1); + gl.getTransform().scale(x, y); pixel_size_stack.back() *= 2. / (fabs(x) + fabs(y)); } void Graphics::translate(float x, float y) { - glTranslatef(x, y, 0); + gl.getTransform().translate(x, y); } void Graphics::shear(float kx, float ky) { - Matrix t; - t.setShear(kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + gl.getTransform().setShear(kx, ky); } void Graphics::origin() { - glLoadIdentity(); + gl.getTransform().setIdentity(); pixel_size_stack.back() = 1; } +Graphics::DisplayState::DisplayState() + : color(255, 255, 255, 255) + , backgroundColor(0, 0, 0, 255) + , blendMode(BLEND_ALPHA) + , lineWidth(1.0f) + , lineStyle(LINE_SMOOTH) + , lineJoin(LINE_JOIN_MITER) + , pointSize(1.0f) + , scissor(false) + , scissorBox() + , font(nullptr) + , shader(nullptr) + , wireframe(false) +{ + // We should just directly initialize the array in the initializer list, but + // that feature of C++11 is broken in Visual Studio 2013... + colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true; +} + +Graphics::DisplayState::DisplayState(const DisplayState &other) + : color(other.color) + , backgroundColor(other.backgroundColor) + , blendMode(other.blendMode) + , lineWidth(other.lineWidth) + , lineStyle(other.lineStyle) + , lineJoin(other.lineJoin) + , pointSize(other.pointSize) + , scissor(other.scissor) + , scissorBox(other.scissorBox) + , font(other.font) + , shader(other.shader) + , canvases(other.canvases) + , wireframe(other.wireframe) +{ + for (int i = 0; i < 4; i++) + colorMask[i] = other.colorMask[i]; +} + +Graphics::DisplayState::~DisplayState() +{ +} + +Graphics::DisplayState &Graphics::DisplayState::operator = (const DisplayState &other) +{ + color = other.color; + backgroundColor = other.backgroundColor; + blendMode = other.blendMode; + lineWidth = other.lineWidth; + lineStyle = other.lineStyle; + lineJoin = other.lineJoin; + pointSize = other.pointSize; + scissor = other.scissor; + scissorBox = other.scissorBox; + + font = other.font; + shader = other.shader; + canvases = other.canvases; + + for (int i = 0; i < 4; i++) + colorMask[i] = other.colorMask[i]; + + wireframe = other.wireframe; + + return *this; +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 49579681d..06ba3535f 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -54,50 +54,6 @@ namespace graphics namespace opengl { -// During display mode changing, certain -// variables about the OpenGL context are -// lost. -struct DisplayState -{ - // Colors. - Color color; - Color backgroundColor; - - // Blend mode. - Graphics::BlendMode blendMode; - - // Line. - Graphics::LineStyle lineStyle; - Graphics::LineJoin lineJoin; - - // Point. - float pointSize; - - // Scissor. - bool scissor; - OpenGL::Viewport scissorBox; - - // Color mask. - bool colorMask[4]; - - bool wireframe; - - // Default values. - DisplayState() - { - color.set(255,255,255,255); - backgroundColor.set(0, 0, 0, 255); - blendMode = Graphics::BLEND_ALPHA; - lineStyle = Graphics::LINE_SMOOTH; - lineJoin = Graphics::LINE_JOIN_MITER; - pointSize = 1.0f; - scissor = false; - colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true; - wireframe = false; - } - -}; - class Graphics : public love::graphics::Graphics { public: @@ -108,10 +64,6 @@ public: // Implements Module. const char *getName() const; - DisplayState saveState(); - - void restoreState(const DisplayState &s); - virtual void setViewportSize(int width, int height); virtual bool setMode(int width, int height, bool &sRGB); virtual void unSetMode(); @@ -244,10 +196,22 @@ public: **/ Font *getFont() const; + void setShader(Shader *shader); + void setShader(); + + Shader *getShader() const; + + void setCanvas(Canvas *canvas); + void setCanvas(const std::vector &canvases); + void setCanvas(const std::vector> &canvases); + void setCanvas(); + + std::vector getCanvas() const; + /** * Sets the enabled color components when rendering. **/ - void setColorMask(bool r, bool g, bool b, bool a); + void setColorMask(const bool mask[4]); /** * Gets the current color mask. @@ -447,8 +411,9 @@ public: **/ bool isSupported(Support feature) const; - void push(); + void push(StackType type = STACK_TRANSFORM); void pop(); + void rotate(float r); void scale(float x, float y = 1.0f); void translate(float x, float y); @@ -457,17 +422,50 @@ public: private: - Font *currentFont; + struct DisplayState + { + // Colors. + Color color; + Color backgroundColor; + + // Blend mode. + BlendMode blendMode; + + // Line. + float lineWidth; + LineStyle lineStyle; + LineJoin lineJoin; + + // Point. + float pointSize; + + // Scissor. + bool scissor; + OpenGL::Viewport scissorBox; + + Object::StrongRef font; + Object::StrongRef shader; + + std::vector> canvases; + + // Color mask. + bool colorMask[4]; + + bool wireframe; + + DisplayState(); + DisplayState(const DisplayState &other); + ~DisplayState(); + + DisplayState &operator = (const DisplayState &other); + }; + + void restoreState(const DisplayState &s); + void restoreStateChecked(const DisplayState &s); + love::window::Window *currentWindow; std::vector pixel_size_stack; // stores current size of a pixel (needed for line drawing) - LineStyle lineStyle; - LineJoin lineJoin; - float lineWidth; - GLint matrixLimit; - GLint userMatrices; - bool colorMask[4]; - bool wireframe; int width; int height; @@ -475,7 +473,10 @@ private: bool activeStencil; - DisplayState savedState; + std::vector states; + std::vector stackTypes; // Keeps track of the pushed stack types. + + static const size_t MAX_USER_STACK_DEPTH = 64; bool displayedMinReqWarning; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 7a391c071..a0d9705f0 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -47,8 +47,6 @@ Image::Image(love::image::ImageData *data, const Flags &flags) { width = data->getWidth(); height = data->getHeight(); - - data->retain(); preload(); } @@ -63,28 +61,22 @@ Image::Image(love::image::CompressedData *cdata, const Flags &flags) { width = cdata->getWidth(0); height = cdata->getHeight(0); - - cdata->retain(); preload(); } Image::~Image() { - if (data != nullptr) - data->release(); - if (cdata != nullptr) - cdata->release(); unload(); } love::image::ImageData *Image::getImageData() const { - return data; + return data.get(); } love::image::CompressedData *Image::getCompressedData() const { - return cdata; + return cdata.get(); } void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) @@ -279,9 +271,9 @@ void Image::uploadTexture() { bind(); - if (isCompressed() && cdata) + if (isCompressed() && cdata.get()) uploadCompressedData(); - else if (data) + else if (data.get()) uploadImageData(); } @@ -290,7 +282,7 @@ bool Image::loadVolatile() if (flags.sRGB && !hasSRGBSupport()) throw love::Exception("sRGB images are not supported on this system."); - if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat())) + if (isCompressed() && cdata.get() && !hasCompressedTextureSupport(cdata->getFormat())) { const char *str; if (image::CompressedData::getConstant(cdata->getFormat(), str)) @@ -397,12 +389,11 @@ void Image::uploadDefaultTexture() void Image::drawv(const Matrix &t, const Vertex *v) { + OpenGL::TempTransform transform(gl); + transform.get() *= t; + predraw(); - glPushMatrix(); - - glMultMatrixf((const GLfloat *)t.getElements()); - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -415,8 +406,6 @@ void Image::drawv(const Matrix &t, const Vertex *v) glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); - postdraw(); } diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 1c8bfc88e..556a728df 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -157,11 +157,11 @@ private: // The ImageData from which the texture is created. May be null if // Compressed image data was used to create the texture. - love::image::ImageData *data; + Object::StrongRef data; // Or the Compressed Image Data from which the texture is created. May be // null if raw ImageData was used to create the texture. - love::image::CompressedData *cdata; + Object::StrongRef cdata; // OpenGL texture identifier. GLuint texture; diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index ec79b4b2c..0e11b2178 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -79,9 +79,6 @@ Mesh::Mesh(int vertexcount, Mesh::DrawMode mode) Mesh::~Mesh() { - if (texture) - texture->release(); - delete vbo; delete ibo; } @@ -264,25 +261,17 @@ size_t Mesh::getVertexMapCount() const void Mesh::setTexture(Texture *tex) { - tex->retain(); - - if (texture) - texture->release(); - - texture = tex; + texture.set(tex); } void Mesh::setTexture() { - if (texture) - texture->release(); - - texture = nullptr; + texture.set(nullptr); } Texture *Mesh::getTexture() const { - return texture; + return texture.get(); } void Mesh::setDrawMode(Mesh::DrawMode mode) @@ -334,7 +323,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo if (vertex_count == 0) return; - if (texture) + if (texture.get()) texture->predraw(); else gl.bindTexture(0); @@ -342,8 +331,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo Matrix m; m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glPushMatrix(); - glMultMatrixf(m.getElements()); + OpenGL::TempTransform transform(gl); + transform.get() *= m; VertexBuffer::Bind vbo_bind(*vbo); @@ -412,9 +401,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo gl.setColor(gl.getColor()); } - glPopMatrix(); - - if (texture) + if (texture.get()) texture->postdraw(); } diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 64b50bd48..5836893de 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -178,7 +178,7 @@ private: int range_min; int range_max; - Texture *texture; + Object::StrongRef texture; // Whether the per-vertex colors are used when drawing. bool colors_enabled; diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 8ab6de22d..88e070e07 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -28,6 +28,7 @@ // C++ #include +#include // C #include @@ -47,6 +48,8 @@ OpenGL::OpenGL() , vendor(VENDOR_UNKNOWN) , state() { + matrices.transform.reserve(10); + matrices.projection.reserve(2); } void OpenGL::initContext() @@ -56,6 +59,7 @@ void OpenGL::initContext() initOpenGLFunctions(); initVendor(); + initMatrices(); contextInitialized = true; } @@ -117,6 +121,15 @@ void OpenGL::setupContext() createDefaultTexture(); state.lastPseudoInstanceID = -1; + + // Invalidate the cached matrices by setting some elements to NaN. + float nan = std::numeric_limits::quiet_NaN(); + state.lastProjectionMatrix.setTranslation(nan, nan); + state.lastTransformMatrix.setTranslation(nan, nan); + + glMatrixMode(GL_MODELVIEW); + + contextInitialized = true; } void OpenGL::deInitContext() @@ -176,6 +189,15 @@ void OpenGL::initMaxValues() maxRenderTargets = std::min(maxattachments, maxdrawbuffers); } +void OpenGL::initMatrices() +{ + matrices.transform.clear(); + matrices.projection.clear(); + + matrices.transform.push_back(Matrix()); + matrices.projection.push_back(Matrix()); +} + void OpenGL::createDefaultTexture() { // Set the 'default' texture (id 0) as a repeating white pixel. Otherwise, @@ -198,6 +220,21 @@ void OpenGL::createDefaultTexture() bindTexture(curtexture); } +void OpenGL::pushTransform() +{ + matrices.transform.push_back(matrices.transform.back()); +} + +void OpenGL::popTransform() +{ + matrices.transform.pop_back(); +} + +Matrix &OpenGL::getTransform() +{ + return matrices.transform.back(); +} + void OpenGL::prepareDraw() { Shader *shader = Shader::current; @@ -218,15 +255,37 @@ void OpenGL::prepareDraw() // We need to make sure antialiased Canvases are properly resolved // before sampling from their textures in a shader. // This is kind of a big hack. :( - const std::map &r = shader->getBoundRetainables(); - for (auto it = r.begin(); it != r.end(); ++it) + for (auto &r : shader->getBoundRetainables()) { // Even bigger hack! D: - Canvas *canvas = dynamic_cast(it->second); + Canvas *canvas = dynamic_cast(r.second); if (canvas != nullptr) canvas->resolveMSAA(); } } + + const float *curproj = matrices.projection.back().getElements(); + const float *lastproj = state.lastProjectionMatrix.getElements(); + + // We only need to re-upload the projection matrix if it's changed. + if (memcmp(curproj, lastproj, sizeof(float) * 16) != 0) + { + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(curproj); + glMatrixMode(GL_MODELVIEW); + + state.lastProjectionMatrix = matrices.projection.back(); + } + + const float *curxform = matrices.transform.back().getElements(); + const float *lastxform = state.lastTransformMatrix.getElements(); + + // Same with the transform matrix. + if (memcmp(curxform, lastxform, sizeof(float) * 16) != 0) + { + glLoadMatrixf(curxform); + state.lastTransformMatrix = matrices.transform.back(); + } } void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 1fd5e4c57..52453255c 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -26,9 +26,11 @@ // LOVE #include "graphics/Color.h" #include "graphics/Texture.h" +#include "common/Matrix.h" // C++ #include +#include // The last argument to AttribPointer takes a buffer offset casted to a pointer. #define BUFFER_OFFSET(i) ((char *) NULL + (i)) @@ -101,6 +103,36 @@ public: GLenum func; }; + struct + { + std::vector transform; + std::vector projection; + } matrices; + + class TempTransform + { + public: + + TempTransform(OpenGL &gl) + : gl(gl) + { + gl.pushTransform(); + } + + ~TempTransform() + { + gl.popTransform(); + } + + Matrix &get() + { + return gl.getTransform(); + } + + private: + OpenGL ≷ + }; + OpenGL(); /** @@ -121,6 +153,10 @@ public: **/ void deInitContext(); + void pushTransform(); + void popTransform(); + Matrix &getTransform(); + /** * Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing. * This *MUST* be called directly before OpenGL drawing functions. @@ -264,6 +300,7 @@ private: void initVendor(); void initOpenGLFunctions(); void initMaxValues(); + void initMatrices(); void createDefaultTexture(); bool contextInitialized; @@ -296,6 +333,9 @@ private: // The last ID value used for pseudo-instancing. int lastPseudoInstanceID; + Matrix lastProjectionMatrix; + Matrix lastTransformMatrix; + } state; }; // OpenGL diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 8c1dd3484..289bcfcc1 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -102,7 +102,6 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size) sizes.push_back(1.0f); colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); setBufferSize(size); - texture->retain(); } ParticleSystem::ParticleSystem(const ParticleSystem &p) @@ -150,22 +149,10 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p) , relativeRotation(p.relativeRotation) { setBufferSize(maxParticles); - - if (texture != nullptr) - texture->retain(); - - for (Quad *quad : quads) - quad->retain(); } ParticleSystem::~ParticleSystem() { - if (texture != nullptr) - texture->release(); - - for (Quad *quad : quads) - quad->release(); - deleteBuffers(); } @@ -422,19 +409,14 @@ ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p) return pNext; } -void ParticleSystem::setTexture(Texture *texture) +void ParticleSystem::setTexture(Texture *tex) { - Object::AutoRelease imagerelease(this->texture); - - this->texture = texture; - - if (texture) - texture->retain(); + texture.set(tex); } Texture *ParticleSystem::getTexture() const { - return texture; + return texture.get(); } void ParticleSystem::setInsertMode(InsertMode mode) @@ -732,26 +714,29 @@ std::vector ParticleSystem::getColor() const void ParticleSystem::setQuads(const std::vector &newQuads) { - for (Quad *quad : newQuads) - quad->retain(); + std::vector> quadlist; + quadlist.reserve(newQuads.size()); - for (Quad *quad : quads) - quad->release(); + for (Quad *q : newQuads) + quadlist.push_back(q); - quads = newQuads; + quads = quadlist; } void ParticleSystem::setQuads() { - for (Quad *quad : quads) - quad->release(); - quads.clear(); } -const std::vector &ParticleSystem::getQuads() const +std::vector ParticleSystem::getQuads() const { - return quads; + std::vector quadlist; + quadlist.reserve(quads.size()); + + for (const Object::StrongRef &q : quads) + quadlist.push_back(q.get()); + + return quadlist; } void ParticleSystem::setRelativeRotation(bool enable) @@ -838,16 +823,16 @@ bool ParticleSystem::isFull() const void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { uint32 pCount = getCount(); - if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr) + if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr) return; Color curcolor = gl.getColor(); - glPushMatrix(); - static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; const Vertex *textureVerts = texture->getVertices(); Vertex *pVerts = particleVerts; @@ -901,8 +886,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo texture->postdraw(); - glPopMatrix(); - gl.setColor(curcolor); } diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index 6f9e8750f..64b7b6244 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -431,7 +431,7 @@ public: /** * Gets the Quads used when drawing the particles. **/ - const std::vector &getQuads() const; + std::vector getQuads() const; /** * sets whether particle angles & rotations are relative to their velocities. @@ -563,7 +563,7 @@ protected: Vertex *particleVerts; // The texture to be drawn. - Texture *texture; + Object::StrongRef texture; // Whether the particle emitter is active. bool active; @@ -640,7 +640,7 @@ protected: std::vector colors; // Quads. - std::vector quads; + std::vector> quads; bool relativeRotation; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index b227d0df6..e548fa07e 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -97,8 +97,8 @@ Shader::~Shader() if (current == this) detach(); - for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it) - it->second->release(); + for (const auto &retainable : boundRetainables) + retainable.second->release(); boundRetainables.clear(); @@ -178,9 +178,8 @@ void Shader::createProgram(const std::vector &shaderids) if (program == 0) throw love::Exception("Cannot create shader program object."); - std::vector::const_iterator it; - for (it = shaderids.begin(); it != shaderids.end(); ++it) - glAttachShader(program, *it); + for (GLuint id : shaderids) + glAttachShader(program, id); // Bind generic vertex attribute indices to names in the shader. for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++) @@ -201,8 +200,8 @@ void Shader::createProgram(const std::vector &shaderids) glLinkProgram(program); // flag shaders for auto-deletion when the program object is deleted. - for (it = shaderids.begin(); it != shaderids.end(); ++it) - glDeleteShader(*it); + for (GLuint id : shaderids) + glDeleteShader(id); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); @@ -275,10 +274,9 @@ bool Shader::loadVolatile() std::vector shaderids; - ShaderSources::const_iterator source; - for (source = shaderSources.begin(); source != shaderSources.end(); ++source) + for (const auto &source : shaderSources) { - GLuint shaderid = compileCode(source->first, source->second); + GLuint shaderid = compileCode(source.first, source.second); shaderids.push_back(shaderid); } @@ -364,11 +362,10 @@ std::string Shader::getWarnings() const const char *typestr; // Get the individual shader stage warnings - std::map::const_iterator it; - for (it = shaderWarnings.begin(); it != shaderWarnings.end(); ++it) + for (const auto &warning : shaderWarnings) { - if (typeNames.find(it->first, typestr)) - warnings += std::string(typestr) + std::string(" shader:\n") + it->second; + if (typeNames.find(warning.first, typestr)) + warnings += std::string(typestr) + std::string(" shader:\n") + warning.second; } warnings += getProgramWarnings(); @@ -380,13 +377,9 @@ void Shader::attach(bool temporary) { if (current != this) { - if (current != nullptr) - current->release(); - glUseProgram(program); current = this; - - current->retain(); + // retain/release happens in Graphics::setShader. } if (!temporary) diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index f9266a477..99d8d79c5 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -88,14 +88,10 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage) delete element_buf; throw love::Exception("Out of memory."); } - - texture->retain(); } SpriteBatch::~SpriteBatch() { - texture->release(); - delete color; delete array_buf; delete element_buf; @@ -172,15 +168,12 @@ void SpriteBatch::flush() void SpriteBatch::setTexture(Texture *newtexture) { - Object::AutoRelease imagerelease(texture); - - newtexture->retain(); - texture = newtexture; + texture.set(newtexture); } Texture *SpriteBatch::getTexture() { - return texture; + return texture.get(); } void SpriteBatch::setColor(const Color &color) @@ -271,11 +264,10 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float return; static Matrix t; - - glPushMatrix(); - t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; texture->predraw(); @@ -314,8 +306,6 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float } texture->postdraw(); - - glPopMatrix(); } void SpriteBatch::addv(const Vertex *v, int index) diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index 1f3de1c99..55a736929 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -126,7 +126,7 @@ private: */ void setColorv(Vertex *v, const Color &color); - Texture *texture; + Object::StrongRef texture; // Max number of sprites in the batch. int size; diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index 91fba30b4..547dead68 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -19,6 +19,7 @@ **/ #include "wrap_Canvas.h" +#include "Graphics.h" namespace love { @@ -37,18 +38,26 @@ int w_Canvas_renderTo(lua_State *L) Canvas *canvas = luax_checkcanvas(L, 1); luaL_checktype(L, 2, LUA_TFUNCTION); - // Save the current Canvas so we can restore it when we're done. - Canvas *oldcanvas = Canvas::current; + Graphics *graphics = Module::getInstance(Module::M_GRAPHICS); - luax_catchexcept(L, [&](){ canvas->startGrab(); }); + if (graphics) + { + // Save the current Canvas so we can restore it when we're done. + std::vector oldcanvases = graphics->getCanvas(); - lua_settop(L, 2); // make sure the function is on top of the stack - lua_call(L, 0, 0); + for (Canvas *c : oldcanvases) + c->retain(); - if (oldcanvas != nullptr) - oldcanvas->startGrab(oldcanvas->getAttachedCanvases()); - else - Canvas::bindDefaultCanvas(); + luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); }); + + lua_settop(L, 2); // make sure the function is on top of the stack + lua_call(L, 0, 0); + + graphics->setCanvas(oldcanvases); + + for (Canvas *c : oldcanvases) + c->release(); + } return 0; } diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 2c09a8bb7..725c96f3a 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -651,8 +651,7 @@ int w_setColorMask(lua_State *L) mask[i] = luax_toboolean(L, i + 1); } - // r, g, b, a - instance()->setColorMask(mask[0], mask[1], mask[2], mask[3]); + instance()->setColorMask(mask); return 0; } @@ -865,43 +864,35 @@ int w_setCanvas(lua_State *L) instance()->discardStencil(); // called with none -> reset to default buffer - if (lua_isnoneornil(L,1)) + if (lua_isnoneornil(L, 1)) { - Canvas::bindDefaultCanvas(); + instance()->setCanvas(); return 0; } bool is_table = lua_istable(L, 1); - std::vector attachments; - - Canvas *canvas = 0; + std::vector canvases; if (is_table) { - // grab the first canvas in the array and attach the rest - lua_rawgeti(L, 1, 1); - canvas = luax_checkcanvas(L, -1); - lua_pop(L, 1); - - for (size_t i = 2; i <= lua_objlen(L, 1); i++) + for (size_t i = 1; i <= lua_objlen(L, 1); i++) { lua_rawgeti(L, 1, i); - attachments.push_back(luax_checkcanvas(L, -1)); + canvases.push_back(luax_checkcanvas(L, -1)); lua_pop(L, 1); } } else { - canvas = luax_checkcanvas(L, 1); - for (int i = 2; i <= lua_gettop(L); i++) - attachments.push_back(luax_checkcanvas(L, i)); + for (int i = 1; i <= lua_gettop(L); i++) + canvases.push_back(luax_checkcanvas(L, i)); } luax_catchexcept(L, [&]() { - if (attachments.size() > 0) - canvas->startGrab(attachments); + if (canvases.size() > 0) + instance()->setCanvas(canvases); else - canvas->startGrab(); + instance()->setCanvas(); }); return 0; @@ -909,24 +900,23 @@ int w_setCanvas(lua_State *L) int w_getCanvas(lua_State *L) { - Canvas *canvas = Canvas::current; - int n = 1; + const std::vector canvases = instance()->getCanvas(); + int n = 0; - if (canvas) + if (!canvases.empty()) { - canvas->retain(); - luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas); - - const std::vector &attachments = canvas->getAttachedCanvases(); - for (size_t i = 0; i < attachments.size(); i++) + for (Canvas *c : canvases) { - attachments[i]->retain(); - luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, attachments[i]); + c->retain(); + luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, c); n++; } } else + { lua_pushnil(L); + n = 1; + } return n; } @@ -935,18 +925,18 @@ int w_setShader(lua_State *L) { if (lua_isnoneornil(L,1)) { - Shader::detach(); + instance()->setShader(); return 0; } Shader *shader = luax_checkshader(L, 1); - shader->attach(); + instance()->setShader(shader); return 0; } int w_getShader(lua_State *L) { - Shader *shader = Shader::current; + Shader *shader = instance()->getShader(); if (shader) { shader->retain(); @@ -1299,7 +1289,12 @@ int w_polygon(lua_State *L) int w_push(lua_State *L) { - luax_catchexcept(L, [&](){ instance()->push(); }); + Graphics::StackType stype = Graphics::STACK_TRANSFORM; + const char *sname = lua_isnoneornil(L, 1) ? nullptr : luaL_checkstring(L, 1); + if (sname && !Graphics::getConstant(sname, stype)) + return luaL_error(L, "Invalid graphics stack type: %s", sname); + + luax_catchexcept(L, [&](){ instance()->push(stype); }); return 0; } diff --git a/src/modules/mouse/sdl/Mouse.cpp b/src/modules/mouse/sdl/Mouse.cpp index 22f88fb05..cf1ccd0c9 100644 --- a/src/modules/mouse/sdl/Mouse.cpp +++ b/src/modules/mouse/sdl/Mouse.cpp @@ -77,11 +77,11 @@ Mouse::Mouse() Mouse::~Mouse() { - if (curCursor) + if (curCursor.get()) setCursor(); - for (auto it = systemCursors.begin(); it != systemCursors.end(); ++it) - it->second->release(); + for (auto &c : systemCursors) + c.second->release(); } love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty) @@ -91,7 +91,7 @@ love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, in love::mouse::Cursor *Mouse::getSystemCursor(Cursor::SystemCursor cursortype) { - Cursor *cursor = NULL; + Cursor *cursor = nullptr; auto it = systemCursors.find(cursortype); if (it != systemCursors.end()) @@ -107,25 +107,19 @@ love::mouse::Cursor *Mouse::getSystemCursor(Cursor::SystemCursor cursortype) void Mouse::setCursor(love::mouse::Cursor *cursor) { - Object::AutoRelease cursorrelease(curCursor); - - curCursor = cursor; - curCursor->retain(); - + curCursor.set(cursor); SDL_SetCursor((SDL_Cursor *) cursor->getHandle()); } void Mouse::setCursor() { - Object::AutoRelease cursorrelease(curCursor); - curCursor = NULL; - + curCursor.set(nullptr); SDL_SetCursor(SDL_GetDefaultCursor()); } love::mouse::Cursor *Mouse::getCursor() const { - return curCursor; + return curCursor.get(); } int Mouse::getX() const diff --git a/src/modules/mouse/sdl/Mouse.h b/src/modules/mouse/sdl/Mouse.h index 43b8dad61..d4ec75c7a 100644 --- a/src/modules/mouse/sdl/Mouse.h +++ b/src/modules/mouse/sdl/Mouse.h @@ -67,7 +67,7 @@ public: private: - love::mouse::Cursor *curCursor; + Object::StrongRef curCursor; std::map systemCursors; diff --git a/src/modules/physics/box2d/Body.cpp b/src/modules/physics/box2d/Body.cpp index 880e50b83..cc18e98bd 100644 --- a/src/modules/physics/box2d/Body.cpp +++ b/src/modules/physics/box2d/Body.cpp @@ -41,7 +41,6 @@ Body::Body(World *world, b2Vec2 p, Body::Type type) { udata = new bodyudata(); udata->ref = nullptr; - world->retain(); b2BodyDef def; def.position = Physics::scaleDown(p); def.userData = (void *) udata; @@ -57,8 +56,7 @@ Body::Body(b2Body *b) , udata(nullptr) { udata = (bodyudata *) b->GetUserData(); - world = (World *)Memoizer::find(b->GetWorld()); - world->retain(); + world.set((World *) Memoizer::find(b->GetWorld())); // Box2D body holds a reference to the love Body. this->retain(); Memoizer::add(body, this); @@ -69,7 +67,6 @@ Body::~Body() if (udata != nullptr) delete udata->ref; delete udata; - world->release(); } float Body::getX() @@ -420,7 +417,7 @@ bool Body::isFixedRotation() const World *Body::getWorld() const { - return world; + return world.get(); } int Body::getFixtureList(lua_State *L) const diff --git a/src/modules/physics/box2d/Body.h b/src/modules/physics/box2d/Body.h index 83dd0c6b1..e951d242b 100644 --- a/src/modules/physics/box2d/Body.h +++ b/src/modules/physics/box2d/Body.h @@ -436,7 +436,7 @@ private: // // This ensures that a World only can be destroyed // once all bodies have been destroyed too. - World *world; + Object::StrongRef world; bodyudata *udata; diff --git a/src/modules/physics/box2d/Joint.cpp b/src/modules/physics/box2d/Joint.cpp index 8ca574530..ee48d202f 100644 --- a/src/modules/physics/box2d/Joint.cpp +++ b/src/modules/physics/box2d/Joint.cpp @@ -40,7 +40,7 @@ namespace box2d { Joint::Joint(Body *body1) - : world(body1->world) + : world(body1->world.get()) , udata(nullptr) , body1(body1) , body2(nullptr) @@ -50,7 +50,7 @@ Joint::Joint(Body *body1) } Joint::Joint(Body *body1, Body *body2) - : world(body1->world) + : world(body1->world.get()) , udata(nullptr) , body1(body1) , body2(body2) diff --git a/src/modules/physics/box2d/World.cpp b/src/modules/physics/box2d/World.cpp index 4f2177543..c08846ec4 100644 --- a/src/modules/physics/box2d/World.cpp +++ b/src/modules/physics/box2d/World.cpp @@ -259,23 +259,20 @@ void World::update(float dt) world->Step(dt, 8, 6); // Destroy all objects marked during the time step. - for (auto i = destructBodies.begin(); i < destructBodies.end(); i++) + for (Body *b : destructBodies) { - Body *b = *i; if (b->body != 0) b->destroy(); // Release for reference in vector. b->release(); } - for (auto i = destructFixtures.begin(); i < destructFixtures.end(); i++) + for (Fixture *f : destructFixtures) { - Fixture *f = *i; if (f->isValid()) f->destroy(); // Release for reference in vector. f->release(); } - for (auto i = destructJoints.begin(); i < destructJoints.end(); i++) + for (Joint *j : destructJoints) { - Joint *j = *i; if (j->isValid()) j->destroyJoint(); // Release for reference in vector. j->release(); diff --git a/src/modules/sound/lullaby/Decoder.cpp b/src/modules/sound/lullaby/Decoder.cpp index 8a591fc8d..62fc45780 100644 --- a/src/modules/sound/lullaby/Decoder.cpp +++ b/src/modules/sound/lullaby/Decoder.cpp @@ -37,7 +37,6 @@ Decoder::Decoder(Data *data, const std::string &ext, int bufferSize) , buffer(0) , eof(false) { - data->retain(); buffer = new char[bufferSize]; } @@ -45,8 +44,6 @@ Decoder::~Decoder() { if (buffer != 0) delete [](char *) buffer; - if (data != 0) - data->release(); } void *Decoder::getBuffer() const diff --git a/src/modules/sound/lullaby/Decoder.h b/src/modules/sound/lullaby/Decoder.h index 4a962960e..ae9571505 100644 --- a/src/modules/sound/lullaby/Decoder.h +++ b/src/modules/sound/lullaby/Decoder.h @@ -53,7 +53,7 @@ protected: // The encoded data. This should be replaced with buffered file // reads in the future. - Data *data; + Object::StrongRef data; // File extension. std::string ext; diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index 6762d9f7b..aa6bcd8bf 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -69,7 +69,7 @@ bool FLACDecoder::accepts(const std::string &ext) love::sound::Decoder *FLACDecoder::clone() { - return new FLACDecoder(data, ext, bufferSize, sampleRate); + return new FLACDecoder(data.get(), ext, bufferSize, sampleRate); } int FLACDecoder::decode() diff --git a/src/modules/sound/lullaby/GmeDecoder.cpp b/src/modules/sound/lullaby/GmeDecoder.cpp index 98d364829..a3131570d 100644 --- a/src/modules/sound/lullaby/GmeDecoder.cpp +++ b/src/modules/sound/lullaby/GmeDecoder.cpp @@ -86,7 +86,7 @@ bool GmeDecoder::accepts(const std::string &ext) love::sound::Decoder *GmeDecoder::clone() { - return new GmeDecoder(data, ext, bufferSize); + return new GmeDecoder(data.get(), ext, bufferSize); } int GmeDecoder::decode() diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index 4e383ea0a..ce62b4acd 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -98,7 +98,7 @@ bool ModPlugDecoder::accepts(const std::string &ext) love::sound::Decoder *ModPlugDecoder::clone() { - return new ModPlugDecoder(data, ext, bufferSize); + return new ModPlugDecoder(data.get(), ext, bufferSize); } int ModPlugDecoder::decode() diff --git a/src/modules/sound/lullaby/Mpg123Decoder.cpp b/src/modules/sound/lullaby/Mpg123Decoder.cpp index 01dc6c83f..1f4a58f08 100644 --- a/src/modules/sound/lullaby/Mpg123Decoder.cpp +++ b/src/modules/sound/lullaby/Mpg123Decoder.cpp @@ -96,7 +96,7 @@ void Mpg123Decoder::quit() love::sound::Decoder *Mpg123Decoder::clone() { - return new Mpg123Decoder(data, ext, bufferSize); + return new Mpg123Decoder(data.get(), ext, bufferSize); } int Mpg123Decoder::decode() diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index 576244d2a..052519204 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -179,7 +179,7 @@ bool VorbisDecoder::accepts(const std::string &ext) love::sound::Decoder *VorbisDecoder::clone() { - return new VorbisDecoder(data, ext, bufferSize); + return new VorbisDecoder(data.get(), ext, bufferSize); } int VorbisDecoder::decode() diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index 84f9fdf3d..5dce78e9d 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -117,7 +117,7 @@ bool WaveDecoder::accepts(const std::string &ext) love::sound::Decoder *WaveDecoder::clone() { - return new WaveDecoder(data, ext, bufferSize); + return new WaveDecoder(data.get(), ext, bufferSize); } int WaveDecoder::decode() diff --git a/src/modules/system/System.cpp b/src/modules/system/System.cpp index dd74c120f..8b1367f8b 100755 --- a/src/modules/system/System.cpp +++ b/src/modules/system/System.cpp @@ -28,6 +28,7 @@ #include //#include //#include +#include #include #elif defined(LOVE_WINDOWS) #include "common/utf8.h" @@ -41,6 +42,20 @@ namespace love namespace system { +System::System() +{ +#if defined(LOVE_LINUX) + // Enable automatic cleanup of zombie processes + struct sigaction act = {0}; + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_DFL; + act.sa_flags = SA_NOCLDWAIT; + + // Requires linux 2.6 or higher, so anything remotely modern + sigaction(SIGCHLD, &act, nullptr); +#endif +} + std::string System::getOS() const { #if defined(LOVE_MACOSX) @@ -86,12 +101,14 @@ bool System::openURL(const std::string &url) const if (posix_spawnp(&pid, "xdg-open", nullptr, nullptr, const_cast(argv), environ) != 0) return false; - // Wait for xdg-open to complete (or fail.) + // Check if xdg-open already completed (or failed.) int status = 0; - if (waitpid(pid, &status, 0) == pid) + if (waitpid(pid, &status, WNOHANG) > 0) return (status == 0); else - return false; + // We can't tell what actually happens without waiting for + // the process to finish, which could take forever (literally). + return true; #elif defined(LOVE_WINDOWS) diff --git a/src/modules/system/System.h b/src/modules/system/System.h index 58eff3356..fb12b22fc 100644 --- a/src/modules/system/System.h +++ b/src/modules/system/System.h @@ -48,6 +48,7 @@ public: POWER_MAX_ENUM }; + System(); virtual ~System() {} // Implements Module. diff --git a/src/modules/thread/LuaThread.cpp b/src/modules/thread/LuaThread.cpp index c05dc830a..5bb6515ea 100644 --- a/src/modules/thread/LuaThread.cpp +++ b/src/modules/thread/LuaThread.cpp @@ -37,14 +37,11 @@ LuaThread::LuaThread(const std::string &name, love::Data *code) , args(0) , nargs(0) { - code->retain(); threadName = name; } LuaThread::~LuaThread() { - code->release(); - // No args should still exist at this point, // but you never know. for (int i = 0; i < nargs; ++i) diff --git a/src/modules/thread/LuaThread.h b/src/modules/thread/LuaThread.h index e5722a750..122c2cc11 100644 --- a/src/modules/thread/LuaThread.h +++ b/src/modules/thread/LuaThread.h @@ -50,7 +50,7 @@ private: void onError(); - love::Data *code; + Object::StrongRef code; std::string name; std::string error; diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 423101211..f2f60372c 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -48,9 +48,6 @@ Window::Window() Window::~Window() { - if (curMode.icon) - curMode.icon->release(); - if (window) SDL_DestroyWindow(window); @@ -185,8 +182,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) } // Make sure the window keeps any previously set icon. - if (window && curMode.icon) - setIcon(curMode.icon); + if (window && curMode.icon.get()) + setIcon(curMode.icon.get()); } if (!window) @@ -553,10 +550,7 @@ bool Window::setIcon(love::image::ImageData *imgd) if (!imgd) return false; - imgd->retain(); - if (curMode.icon) - curMode.icon->release(); - curMode.icon = imgd; + curMode.icon.set(imgd); if (!window) return false; @@ -597,7 +591,7 @@ bool Window::setIcon(love::image::ImageData *imgd) love::image::ImageData *Window::getIcon() { - return curMode.icon; + return curMode.icon.get(); } void Window::minimize() diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index 73421b9c1..4a769cae5 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -111,7 +111,7 @@ private: int width; int height; WindowSettings settings; - love::image::ImageData *icon; + Object::StrongRef icon; } curMode;