Browse Source

Merge branch '12.0-development' into GraphicsBuffer

Alex Szpakowski 5 years ago
parent
commit
7fbb68e90e

+ 14 - 0
src/common/Object.h

@@ -78,6 +78,20 @@ private:
 
 }; // Object
 
+/**
+ * Structure wrapping an object and its associated Type instance. This is used
+ * for storing everything necessary to identify an object's properties in
+ * environments where the Type is not easily obtained otherwise, for example in
+ * a Lua state.
+ **/
+struct Proxy
+{
+	// Holds type information (see types.h).
+	love::Type *type;
+
+	// Pointer to the actual object.
+	Object *object;
+};
 
 enum class Acquire
 {

+ 4 - 153
src/common/Variant.cpp

@@ -26,25 +26,13 @@
 namespace love
 {
 
-static Proxy *tryextractproxy(lua_State *L, int idx)
-{
-	Proxy *u = (Proxy *)lua_touserdata(L, idx);
-
-	if (u == nullptr || u->type == nullptr)
-		return nullptr;
-
-	// We could get rid of the dynamic_cast for more performance, but it would
-	// be less safe...
-	if (dynamic_cast<Object *>(u->object) != nullptr)
-		return u;
-
-	return nullptr;
-}
+Variant::Variant(Type vtype)
+	: type(vtype)
+{}
 
 Variant::Variant()
 	: type(NIL)
-{
-}
+{}
 
 Variant::Variant(bool boolean)
 	: type(BOOLEAN)
@@ -152,141 +140,4 @@ Variant &Variant::operator = (const Variant &v)
 	return *this;
 }
 
-Variant Variant::fromLua(lua_State *L, int n, bool allowuserdata, std::set<const void*> *tableSet)
-{
-	size_t len;
-	const char *str;
-	Proxy *p = nullptr;
-
-	if (n < 0) // Fix the stack position, we might modify it later
-		n += lua_gettop(L) + 1;
-
-	switch (lua_type(L, n))
-	{
-	case LUA_TBOOLEAN:
-		return Variant(luax_toboolean(L, n));
-	case LUA_TNUMBER:
-		return Variant(lua_tonumber(L, n));
-	case LUA_TSTRING:
-		str = lua_tolstring(L, n, &len);
-		return Variant(str, len);
-	case LUA_TLIGHTUSERDATA:
-		return Variant(lua_touserdata(L, n));
-	case LUA_TUSERDATA:
-		if (!allowuserdata)
-		{
-			luax_typerror(L, n, "copyable Lua value");
-			return Variant();
-		}
-		p = tryextractproxy(L, n);
-		if (p != nullptr)
-			return Variant(p->type, p->object);
-		else
-		{
-			luax_typerror(L, n, "love type");
-			return Variant();
-		}
-	case LUA_TNIL:
-		return Variant();
-	case LUA_TTABLE:
-		{
-			bool success = true;
-			std::set<const void *> topTableSet;
-
-			// We can use a pointer to a stack-allocated variable because it's
-			// never used after the stack-allocated variable is destroyed.
-			if (tableSet == nullptr)
-				tableSet = &topTableSet;
-
-			// Now make sure this table wasn't already serialised
-			const void *tablePointer = lua_topointer(L, n);
-			{
-				auto result = tableSet->insert(tablePointer);
-				if (!result.second) // insertion failed
-					throw love::Exception("Cycle detected in table");
-			}
-
-			SharedTable *table = new SharedTable();
-
-			size_t len = luax_objlen(L, -1);
-			if (len > 0)
-				table->pairs.reserve(len);
-
-			lua_pushnil(L);
-
-			while (lua_next(L, n))
-			{
-				table->pairs.emplace_back(fromLua(L, -2, allowuserdata, tableSet), fromLua(L, -1, allowuserdata, tableSet));
-				lua_pop(L, 1);
-
-				const auto &p = table->pairs.back();
-				if (p.first.getType() == UNKNOWN || p.second.getType() == UNKNOWN)
-				{
-					success = false;
-					break;
-				}
-			}
-
-			// And remove the table from the set again
-			tableSet->erase(tablePointer);
-
-			if (success)
-				return Variant(table);
-			else
-				table->release();
-		}
-		break;
-	}
-
-	Variant v;
-	v.type = UNKNOWN;
-	return v;
-}
-
-void Variant::toLua(lua_State *L) const
-{
-	switch (type)
-	{
-	case BOOLEAN:
-		lua_pushboolean(L, data.boolean);
-		break;
-	case NUMBER:
-		lua_pushnumber(L, data.number);
-		break;
-	case STRING:
-		lua_pushlstring(L, data.string->str, data.string->len);
-		break;
-	case SMALLSTRING:
-		lua_pushlstring(L, data.smallstring.str, data.smallstring.len);
-		break;
-	case LUSERDATA:
-		lua_pushlightuserdata(L, data.userdata);
-		break;
-	case LOVEOBJECT:
-		luax_pushtype(L, *data.objectproxy.type, data.objectproxy.object);
-		break;
-	case TABLE:
-	{
-		std::vector<std::pair<Variant, Variant>> &table = data.table->pairs;
-		int tsize = (int) table.size();
-
-		lua_createtable(L, 0, tsize);
-
-		for (int i = 0; i < tsize; ++i)
-		{
-			std::pair<Variant, Variant> &kv = table[i];
-			kv.first.toLua(L);
-			kv.second.toLua(L);
-			lua_settable(L, -3);
-		}
-
-		break;
-	}
-	case NIL:
-	default:
-		lua_pushnil(L);
-		break;
-	}
-}
-
 } // love

+ 3 - 4
src/common/Variant.h

@@ -22,14 +22,12 @@
 #define LOVE_VARIANT_H
 
 #include "common/config.h"
-#include "common/runtime.h"
 #include "common/Object.h"
 #include "common/int.h"
 
 #include <cstring>
 #include <string>
 #include <vector>
-#include <set>
 
 namespace love
 {
@@ -112,11 +110,12 @@ public:
 	Type getType() const { return type; }
 	const Data &getData() const { return data; }
 
-	static Variant fromLua(lua_State *L, int n, bool allowuserdata = true, std::set<const void*> *tableSet = nullptr);
-	void toLua(lua_State *L) const;
+	static Variant unknown() { return Variant(UNKNOWN); }
 
 private:
 
+	Variant(Type vtype);
+
 	Type type;
 	Data data;
 

+ 154 - 0
src/common/runtime.cpp

@@ -661,6 +661,160 @@ bool luax_istype(lua_State *L, int idx, love::Type &type)
 		return false;
 }
 
+static Proxy *tryextractproxy(lua_State *L, int idx)
+{
+	Proxy *u = (Proxy *)lua_touserdata(L, idx);
+
+	if (u == nullptr || u->type == nullptr)
+		return nullptr;
+
+	// We could get rid of the dynamic_cast for more performance, but it would
+	// be less safe...
+	if (dynamic_cast<Object *>(u->object) != nullptr)
+		return u;
+
+	return nullptr;
+}
+
+Variant luax_checkvariant(lua_State *L, int n, bool allowuserdata, std::set<const void*> *tableSet)
+{
+	size_t len;
+	const char *str;
+	Proxy *p = nullptr;
+
+	if (n < 0) // Fix the stack position, we might modify it later
+		n += lua_gettop(L) + 1;
+
+	switch (lua_type(L, n))
+	{
+	case LUA_TBOOLEAN:
+		return Variant(luax_toboolean(L, n));
+	case LUA_TNUMBER:
+		return Variant(lua_tonumber(L, n));
+	case LUA_TSTRING:
+		str = lua_tolstring(L, n, &len);
+		return Variant(str, len);
+	case LUA_TLIGHTUSERDATA:
+		return Variant(lua_touserdata(L, n));
+	case LUA_TUSERDATA:
+		if (!allowuserdata)
+		{
+			luax_typerror(L, n, "copyable Lua value");
+			return Variant();
+		}
+		p = tryextractproxy(L, n);
+		if (p != nullptr)
+			return Variant(p->type, p->object);
+		else
+		{
+			luax_typerror(L, n, "love type");
+			return Variant();
+		}
+	case LUA_TNIL:
+		return Variant();
+	case LUA_TTABLE:
+		{
+			bool success = true;
+			std::set<const void *> topTableSet;
+
+			// We can use a pointer to a stack-allocated variable because it's
+			// never used after the stack-allocated variable is destroyed.
+			if (tableSet == nullptr)
+				tableSet = &topTableSet;
+
+			// Now make sure this table wasn't already serialised
+			const void *tablePointer = lua_topointer(L, n);
+			{
+				auto result = tableSet->insert(tablePointer);
+				if (!result.second) // insertion failed
+					throw love::Exception("Cycle detected in table");
+			}
+
+			Variant::SharedTable *table = new Variant::SharedTable();
+
+			size_t len = luax_objlen(L, -1);
+			if (len > 0)
+				table->pairs.reserve(len);
+
+			lua_pushnil(L);
+
+			while (lua_next(L, n))
+			{
+				table->pairs.emplace_back(
+					luax_checkvariant(L, -2, allowuserdata, tableSet),
+					luax_checkvariant(L, -1, allowuserdata, tableSet)
+				);
+				lua_pop(L, 1);
+
+				const auto &p = table->pairs.back();
+				if (p.first.getType() == Variant::UNKNOWN || p.second.getType() == Variant::UNKNOWN)
+				{
+					success = false;
+					break;
+				}
+			}
+
+			// And remove the table from the set again
+			tableSet->erase(tablePointer);
+
+			if (success)
+				return Variant(table);
+			else
+				table->release();
+		}
+		break;
+	}
+
+	return Variant::unknown();
+}
+
+void luax_pushvariant(lua_State *L, const Variant &v)
+{
+	const Variant::Data &data = v.getData();
+	switch (v.getType())
+	{
+	case Variant::BOOLEAN:
+		lua_pushboolean(L, data.boolean);
+		break;
+	case Variant::NUMBER:
+		lua_pushnumber(L, data.number);
+		break;
+	case Variant::STRING:
+		lua_pushlstring(L, data.string->str, data.string->len);
+		break;
+	case Variant::SMALLSTRING:
+		lua_pushlstring(L, data.smallstring.str, data.smallstring.len);
+		break;
+	case Variant::LUSERDATA:
+		lua_pushlightuserdata(L, data.userdata);
+		break;
+	case Variant::LOVEOBJECT:
+		luax_pushtype(L, *data.objectproxy.type, data.objectproxy.object);
+		break;
+	case Variant::TABLE:
+	{
+		std::vector<std::pair<Variant, Variant>> &table = data.table->pairs;
+		int tsize = (int) table.size();
+
+		lua_createtable(L, 0, tsize);
+
+		for (int i = 0; i < tsize; ++i)
+		{
+			std::pair<Variant, Variant> &kv = table[i];
+			luax_pushvariant(L, kv.first);
+			luax_pushvariant(L, kv.second);
+			lua_settable(L, -3);
+		}
+
+		break;
+	}
+	case Variant::NIL:
+	default:
+		lua_pushnil(L);
+		break;
+	}
+}
+
 int luax_getfunction(lua_State *L, const char *mod, const char *fn)
 {
 	lua_getglobal(L, "love");

+ 13 - 16
src/common/runtime.h

@@ -24,6 +24,8 @@
 // LOVE
 #include "config.h"
 #include "types.h"
+#include "Object.h"
+#include "Variant.h"
 #include "deprecation.h"
 
 // Lua
@@ -37,12 +39,12 @@ extern "C" {
 // C++
 #include <exception>
 #include <algorithm>
+#include <set>
 
 namespace love
 {
 
 // Forward declarations.
-class Object;
 class Module;
 class Reference;
 
@@ -59,21 +61,6 @@ enum Registry
 	REGISTRY_OBJECTS
 };
 
-/**
- * This structure wraps all Lua-exposed objects. It exists in the
- * Lua state as a full userdata (so we can catch __gc "events"),
- * though the Object it refers to is light userdata in the sense
- * that it is not allocated by the Lua VM.
- **/
-struct Proxy
-{
-	// Holds type information (see types.h).
-	love::Type *type;
-
-	// Pointer to the actual object.
-	Object *object;
-};
-
 /**
  * A Module with Lua wrapper functions and other data.
  **/
@@ -365,6 +352,16 @@ void luax_pushtype(lua_State *L, StrongRef<T> &object)
  **/
 void luax_rawnewtype(lua_State *L, love::Type &type, love::Object *object);
 
+/**
+ * Stores the value at the given index on the stack into a Variant object.
+ */
+LOVE_EXPORT Variant luax_checkvariant(lua_State *L, int idx, bool allowuserdata = true, std::set<const void*> *tableSet = nullptr);
+
+/**
+ * Pushes the contents of the given Variant index onto the stack.
+ */
+LOVE_EXPORT void luax_pushvariant(lua_State *L, const Variant &v);
+
 /**
  * Checks whether the value at idx is a certain type.
  * @param L The Lua state.

+ 2 - 2
src/love.cpp

@@ -205,7 +205,7 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res
 	}
 
 	// Set love.restart = restartvalue, and clear restartvalue.
-	restartvalue.toLua(L);
+	love::luax_pushvariant(L, restartvalue);
 	lua_setfield(L, -2, "restart");
 	restartvalue = love::Variant();
 
@@ -240,7 +240,7 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res
 		// Disallow userdata (love objects) from being referenced by the restart
 		// value.
 		if (retidx < lua_gettop(L))
-			restartvalue = love::Variant::fromLua(L, retidx + 1, false);
+			restartvalue = love::luax_checkvariant(L, retidx + 1, false);
 	}
 
 	lua_close(L);

+ 0 - 40
src/modules/event/Event.cpp

@@ -38,46 +38,6 @@ Message::~Message()
 {
 }
 
-int Message::toLua(lua_State *L)
-{
-	luax_pushstring(L, name);
-
-	for (const Variant &v : args)
-		v.toLua(L);
-
-	return (int) args.size() + 1;
-}
-
-Message *Message::fromLua(lua_State *L, int n)
-{
-	std::string name = luax_checkstring(L, n);
-	std::vector<Variant> vargs;
-
-	int count = lua_gettop(L) - n;
-	n++;
-
-	Variant varg;
-
-	for (int i = 0; i < count; i++)
-	{
-		if (lua_isnoneornil(L, n+i))
-			break;
-
-		luax_catchexcept(L, [&]() {
-			vargs.push_back(Variant::fromLua(L, n+i));
-		});
-
-		if (vargs.back().getType() == Variant::UNKNOWN)
-		{
-			vargs.clear();
-			luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
-			return nullptr;
-		}
-	}
-
-	return new Message(name, vargs);
-}
-
 Event::~Event()
 {
 }

+ 0 - 3
src/modules/event/Event.h

@@ -46,9 +46,6 @@ public:
 	Message(const std::string &name, const std::vector<Variant> &vargs = {});
 	~Message();
 
-	int toLua(lua_State *L);
-	static Message *fromLua(lua_State *L, int n);
-
 	const std::string name;
 	const std::vector<Variant> args;
 

+ 34 - 11
src/modules/event/wrap_Event.cpp

@@ -38,13 +38,23 @@ namespace event
 
 #define instance() (Module::getInstance<Event>(Module::M_EVENT))
 
+static int luax_pushmessage(lua_State *L, const Message &m)
+{
+	luax_pushstring(L, m.name);
+
+	for (const Variant &v : m.args)
+		luax_pushvariant(L, v);
+
+	return (int) m.args.size() + 1;
+}
+
 static int w_poll_i(lua_State *L)
 {
 	Message *m = nullptr;
 
-	if (instance()->poll(m))
+	if (instance()->poll(m) && m != nullptr)
 	{
-		int args = m->toLua(L);
+		int args = luax_pushmessage(L, *m);
 		m->release();
 		return args;
 	}
@@ -63,9 +73,9 @@ int w_wait(lua_State *L)
 {
 	Message *m = nullptr;
 	luax_catchexcept(L, [&]() { m = instance()->wait(); });
-	if (m)
+	if (m != nullptr)
 	{
-		int args = m->toLua(L);
+		int args = luax_pushmessage(L, *m);
 		m->release();
 		return args;
 	}
@@ -75,15 +85,28 @@ int w_wait(lua_State *L)
 
 int w_push(lua_State *L)
 {
-	StrongRef<Message> m;
-	luax_catchexcept(L, [&]() { m.set(Message::fromLua(L, 1), Acquire::NORETAIN); });
+	std::string name = luax_checkstring(L, 1);
+	std::vector<Variant> vargs;
 
-	luax_pushboolean(L, m.get() != nullptr);
+	int nargs = lua_gettop(L);
+	for (int i = 2; i <= nargs; i++)
+	{
+		if (lua_isnoneornil(L, i))
+			break;
+
+		luax_catchexcept(L, [&]() { vargs.push_back(luax_checkvariant(L, i)); });
 
-	if (m.get() == nullptr)
-		return 1;
+		if (vargs.back().getType() == Variant::UNKNOWN)
+		{
+			vargs.clear();
+			return luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", i);
+		}
+	}
+
+	StrongRef<Message> m(new Message(name, vargs), Acquire::NORETAIN);
 
 	instance()->push(m);
+	luax_pushboolean(L, true);
 	return 1;
 }
 
@@ -98,7 +121,7 @@ int w_quit(lua_State *L)
 	luax_catchexcept(L, [&]() {
 		std::vector<Variant> args;
 		for (int i = 1; i <= std::max(1, lua_gettop(L)); i++)
-			args.push_back(Variant::fromLua(L, i));
+			args.push_back(luax_checkvariant(L, i));
 
 		StrongRef<Message> m(new Message("quit", args), Acquire::NORETAIN);
 		instance()->push(m);
@@ -115,7 +138,7 @@ int w_restart(lua_State *L)
 		args.emplace_back("restart", strlen("restart"));
 
 		for (int i = 1; i <= lua_gettop(L); i++)
-			args.push_back(Variant::fromLua(L, i));
+			args.push_back(luax_checkvariant(L, i));
 
 		StrongRef<Message> m(new Message("quit", args), Acquire::NORETAIN);
 		instance()->push(m);

+ 1 - 0
src/modules/event/wrap_Event.h

@@ -24,6 +24,7 @@
 // LOVE
 #include "common/config.h"
 #include "Event.h"
+#include "common/runtime.h"
 
 namespace love
 {

+ 1 - 1
src/modules/graphics/Deprecations.cpp

@@ -82,7 +82,7 @@ void Deprecations::draw(Graphics *gfx)
 		font.set(gfx->newDefaultFont(9, hinting), Acquire::NORETAIN);
 	}
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	gfx->push(Graphics::STACK_ALL);
 	gfx->reset();

+ 3 - 3
src/modules/graphics/Font.cpp

@@ -133,7 +133,7 @@ bool Font::loadVolatile()
 void Font::createTexture()
 {
 	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	Texture *texture = nullptr;
 	TextureSize size = {textureWidth, textureHeight};
@@ -645,13 +645,13 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
 
 	for (const DrawCommand &cmd : drawcommands)
 	{
-		Graphics::StreamDrawCommand streamcmd;
+		Graphics::BatchedDrawCommand streamcmd;
 		streamcmd.formats[0] = vertexFormat;
 		streamcmd.indexMode = TRIANGLEINDEX_QUADS;
 		streamcmd.vertexCount = cmd.vertexcount;
 		streamcmd.texture = cmd.texture;
 
-		Graphics::StreamVertexData data = gfx->requestStreamDraw(streamcmd);
+		Graphics::BatchedVertexData data = gfx->requestBatchedDraw(streamcmd);
 		GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0];
 
 		memcpy(vertexdata, &vertices[cmd.startvertex], sizeof(GlyphVertex) * cmd.vertexcount);

+ 21 - 21
src/modules/graphics/Graphics.cpp

@@ -114,7 +114,7 @@ Graphics::Graphics()
 	, created(false)
 	, active(true)
 	, writingToStencil(false)
-	, streamBufferState()
+	, batchedDrawState()
 	, projectionMatrix()
 	, renderTargetSwitchCount(0)
 	, drawCalls(0)
@@ -157,9 +157,9 @@ Graphics::~Graphics()
 
 	defaultFont.set(nullptr);
 
-	delete streamBufferState.vb[0];
-	delete streamBufferState.vb[1];
-	delete streamBufferState.indexBuffer;
+	delete batchedDrawState.vb[0];
+	delete batchedDrawState.vb[1];
+	delete batchedDrawState.indexBuffer;
 
 	for (int i = 0; i < (int) ShaderStage::STAGE_MAX_ENUM; i++)
 		cachedShaderStages[i].clear();
@@ -694,7 +694,7 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
 	int w = firsttex->getWidth(firsttarget.mipmap);
 	int h = firsttex->getHeight(firsttarget.mipmap);
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	if (rts.depthStencil.texture == nullptr && rts.temporaryRTFlags != 0)
 	{
@@ -744,7 +744,7 @@ void Graphics::setRenderTarget()
 	if (state.renderTargets.colors.empty() && state.renderTargets.depthStencil.texture == nullptr)
 		return;
 
-	flushStreamDraws();
+	flushBatchedDraws();
 	setRenderTargetsInternal(RenderTargets(), width, height, pixelWidth, pixelHeight, isGammaCorrect());
 
 	state.renderTargets = RenderTargetsStrongRef();
@@ -989,9 +989,9 @@ void Graphics::captureScreenshot(const ScreenshotInfo &info)
 	pendingScreenshotCallbacks.push_back(info);
 }
 
-Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &cmd)
+Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd)
 {
-	StreamBufferState &state = streamBufferState;
+	BatchedDrawState &state = batchedDrawState;
 
 	bool shouldflush = false;
 	bool shouldresize = false;
@@ -1053,7 +1053,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 
 	if (shouldflush || shouldresize)
 	{
-		flushStreamDraws();
+		flushBatchedDraws();
 
 		state.primitiveMode = cmd.primitiveMode;
 		state.formats[0] = cmd.formats[0];
@@ -1097,7 +1097,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 		state.indexBufferMap.data += reqIndexSize;
 	}
 
-	StreamVertexData d;
+	BatchedVertexData d;
 
 	for (int i = 0; i < 2; i++)
 	{
@@ -1121,9 +1121,9 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 	return d;
 }
 
-void Graphics::flushStreamDraws()
+void Graphics::flushBatchedDraws()
 {
-	auto &sbstate = streamBufferState;
+	auto &sbstate = batchedDrawState;
 
 	if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
 		return;
@@ -1194,15 +1194,15 @@ void Graphics::flushStreamDraws()
 	if (attributes.isEnabled(ATTRIB_COLOR))
 		setColor(nc);
 
-	streamBufferState.vertexCount = 0;
-	streamBufferState.indexCount = 0;
+	batchedDrawState.vertexCount = 0;
+	batchedDrawState.indexCount = 0;
 }
 
-void Graphics::flushStreamDrawsGlobal()
+void Graphics::flushBatchedDrawsGlobal()
 {
 	Graphics *instance = getInstance<Graphics>(M_GRAPHICS);
 	if (instance != nullptr)
-		instance->flushStreamDraws();
+		instance->flushBatchedDraws();
 }
 
 /**
@@ -1269,13 +1269,13 @@ void Graphics::points(const Vector2 *positions, const Colorf *colors, size_t num
 	const Matrix4 &t = getTransform();
 	bool is2D = t.isAffine2DTransform();
 
-	StreamDrawCommand cmd;
+	BatchedDrawCommand cmd;
 	cmd.primitiveMode = PRIMITIVE_POINTS;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::RGBAub;
 	cmd.vertexCount = (int) numpoints;
 
-	StreamVertexData data = requestStreamDraw(cmd);
+	BatchedVertexData data = requestBatchedDraw(cmd);
 
 	if (is2D)
 		t.transformXY((Vector2 *) data.stream[0], positions, cmd.vertexCount);
@@ -1565,13 +1565,13 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool
 		const Matrix4 &t = getTransform();
 		bool is2D = t.isAffine2DTransform();
 
-		StreamDrawCommand cmd;
+		BatchedDrawCommand cmd;
 		cmd.formats[0] = getSinglePositionFormat(is2D);
 		cmd.formats[1] = CommonFormat::RGBAub;
 		cmd.indexMode = TRIANGLEINDEX_FAN;
 		cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
 
-		StreamVertexData data = requestStreamDraw(cmd);
+		BatchedVertexData data = requestBatchedDraw(cmd);
 
 		if (is2D)
 			t.transformXY((Vector2 *) data.stream[0], coords, cmd.vertexCount);
@@ -1597,7 +1597,7 @@ Graphics::Stats Graphics::getStats() const
 	getAPIStats(stats.shaderSwitches);
 
 	stats.drawCalls = drawCalls;
-	if (streamBufferState.vertexCount > 0)
+	if (batchedDrawState.vertexCount > 0)
 		stats.drawCalls++;
 
 	stats.renderTargetSwitches = renderTargetSwitchCount;

+ 9 - 9
src/modules/graphics/Graphics.h

@@ -253,7 +253,7 @@ public:
 		{}
 	};
 
-	struct StreamDrawCommand
+	struct BatchedDrawCommand
 	{
 		PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
 		CommonFormat formats[2];
@@ -262,14 +262,14 @@ public:
 		Texture *texture = nullptr;
 		Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
 
-		StreamDrawCommand()
+		BatchedDrawCommand()
 		{
 			// VS2013 can't initialize arrays in the above manner...
 			formats[1] = formats[0] = CommonFormat::NONE;
 		}
 	};
 
-	struct StreamVertexData
+	struct BatchedVertexData
 	{
 		void *stream[2];
 	};
@@ -822,10 +822,10 @@ public:
 	virtual void draw(const DrawIndexedCommand &cmd) = 0;
 	virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
 
-	void flushStreamDraws();
-	StreamVertexData requestStreamDraw(const StreamDrawCommand &command);
+	void flushBatchedDraws();
+	BatchedVertexData requestBatchedDraw(const BatchedDrawCommand &command);
 
-	static void flushStreamDrawsGlobal();
+	static void flushBatchedDrawsGlobal();
 
 	virtual Shader::Language getShaderLanguageTarget() const = 0;
 	const DefaultShaderCode &getCurrentDefaultShaderCode() const;
@@ -911,7 +911,7 @@ protected:
 		SamplerState defaultSamplerState = SamplerState();
 	};
 
-	struct StreamBufferState
+	struct BatchedDrawState
 	{
 		StreamBuffer *vb[2];
 		StreamBuffer *indexBuffer = nullptr;
@@ -926,7 +926,7 @@ protected:
 		StreamBuffer::MapInfo vbMap[2];
 		StreamBuffer::MapInfo indexBufferMap = StreamBuffer::MapInfo();
 
-		StreamBufferState()
+		BatchedDrawState()
 		{
 			vb[0] = vb[1] = nullptr;
 			formats[0] = formats[1] = CommonFormat::NONE;
@@ -979,7 +979,7 @@ protected:
 
 	std::vector<ScreenshotInfo> pendingScreenshotCallbacks;
 
-	StreamBufferState streamBufferState;
+	BatchedDrawState batchedDrawState;
 
 	std::vector<Matrix4> transformStack;
 	Matrix4 projectionMatrix;

+ 1 - 1
src/modules/graphics/Mesh.cpp

@@ -552,7 +552,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 	if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
 		throw love::Exception("Instancing is not supported on this system.");
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	if (Shader::isDefaultActive())
 		Shader::attachDefault(Shader::STANDARD_DEFAULT);

+ 1 - 1
src/modules/graphics/ParticleSystem.cpp

@@ -1032,7 +1032,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
 	if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
 		return;
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	if (Shader::isDefaultActive())
 		Shader::attachDefault(Shader::STANDARD_DEFAULT);

+ 2 - 2
src/modules/graphics/Polyline.cpp

@@ -390,13 +390,13 @@ void Polyline::draw(love::graphics::Graphics *gfx)
 	{
 		const Vector2 *verts = vertices + vertex_start;
 
-		Graphics::StreamDrawCommand cmd;
+		Graphics::BatchedDrawCommand cmd;
 		cmd.formats[0] = getSinglePositionFormat(is2D);
 		cmd.formats[1] = CommonFormat::RGBAub;
 		cmd.indexMode = triangle_mode;
 		cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start);
 
-		Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
+		Graphics::BatchedVertexData data = gfx->requestBatchedDraw(cmd);
 
 		if (is2D)
 			t.transformXY((Vector2 *) data.stream[0], verts, cmd.vertexCount);

+ 1 - 1
src/modules/graphics/SpriteBatch.cpp

@@ -302,7 +302,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	if (next == 0)
 		return;
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	if (texture.get())
 	{

+ 1 - 1
src/modules/graphics/Text.cpp

@@ -249,7 +249,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 	if (vertex_buffer == nullptr || draw_commands.empty())
 		return;
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 
 	if (Shader::isDefaultActive())
 		Shader::attachDefault(Shader::STANDARD_DEFAULT);

+ 7 - 7
src/modules/graphics/Texture.cpp

@@ -322,14 +322,14 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
 	const Matrix4 &tm = gfx->getTransform();
 	bool is2D = tm.isAffine2DTransform();
 
-	Graphics::StreamDrawCommand cmd;
+	Graphics::BatchedDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
 	cmd.indexMode = TRIANGLEINDEX_QUADS;
 	cmd.vertexCount = 4;
 	cmd.texture = this;
 
-	Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
+	Graphics::BatchedVertexData data = gfx->requestBatchedDraw(cmd);
 
 	Matrix4 t(tm, localTransform);
 
@@ -377,7 +377,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
 
 	Matrix4 t(tm, m);
 
-	Graphics::StreamDrawCommand cmd;
+	Graphics::BatchedDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STPf_RGBAub;
 	cmd.indexMode = TRIANGLEINDEX_QUADS;
@@ -385,7 +385,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
 	cmd.texture = this;
 	cmd.standardShaderType = Shader::STANDARD_ARRAY;
 
-	Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
+	Graphics::BatchedVertexData data = gfx->requestBatchedDraw(cmd);
 
 	if (is2D)
 		t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
@@ -460,7 +460,7 @@ void Texture::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap
 	if (isPixelFormatCompressed(d->getFormat()) && (rect.x != 0 || rect.y != 0 || rect.w != mipw || rect.h != miph))
 		throw love::Exception("Compressed textures only support replacing the entire Texture.");
 
-	Graphics::flushStreamDrawsGlobal();
+	Graphics::flushBatchedDrawsGlobal();
 
 	uploadImageData(d, mipmap, slice, x, y);
 
@@ -477,7 +477,7 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap
 	if (gfx != nullptr && gfx->isRenderTargetActive(this))
 		return;
 
-	Graphics::flushStreamDrawsGlobal();
+	Graphics::flushBatchedDrawsGlobal();
 
 	uploadByteData(format, data, size, mipmap, slice, rect, nullptr);
 
@@ -627,7 +627,7 @@ void Texture::setSamplerState(const SamplerState &s)
 	if (s.depthSampleMode.hasValue && !isPixelFormatDepth(format))
 		throw love::Exception("Only depth textures can have a depth sample compare mode.");
 
-	Graphics::flushStreamDrawsGlobal();
+	Graphics::flushBatchedDrawsGlobal();
 
 	samplerState = s;
 

+ 3 - 3
src/modules/graphics/Video.cpp

@@ -120,14 +120,14 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
 
 	Matrix4 t(tm, m);
 
-	Graphics::StreamDrawCommand cmd;
+	Graphics::BatchedDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
 	cmd.indexMode = TRIANGLEINDEX_QUADS;
 	cmd.vertexCount = 4;
 	cmd.standardShaderType = Shader::STANDARD_VIDEO;
 
-	Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
+	Graphics::BatchedVertexData data = gfx->requestBatchedDraw(cmd);
 
 	if (is2D)
 		t.transformXY((Vector2 *) data.stream[0], vertices, 4);
@@ -148,7 +148,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
 	if (Shader::current != nullptr)
 		Shader::current->setVideoTextures(textures[0], textures[1], textures[2]);
 
-	gfx->flushStreamDraws();
+	gfx->flushBatchedDraws();
 }
 
 void Video::update()

+ 25 - 25
src/modules/graphics/opengl/Graphics.cpp

@@ -234,13 +234,13 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 
 	setDebug(isDebugEnabled());
 
-	if (streamBufferState.vb[0] == nullptr)
+	if (batchedDrawState.vb[0] == nullptr)
 	{
 		// Initial sizes that should be good enough for most cases. It will
 		// resize to fit if needed, later.
-		streamBufferState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
-		streamBufferState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256  * 1024 * 1);
-		streamBufferState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
+		batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
+		batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256  * 1024 * 1);
+		batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
 	}
 
 	// Reload all volatile objects.
@@ -293,7 +293,7 @@ void Graphics::unSetMode()
 	if (!isCreated())
 		return;
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	// Unload all volatile objects. These must be reloaded after the display
 	// mode change.
@@ -321,7 +321,7 @@ void Graphics::unSetMode()
 
 void Graphics::setActive(bool enable)
 {
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	// Make sure all pending OpenGL commands have fully executed before
 	// returning, when going from active to inactive. This is required on iOS.
@@ -505,7 +505,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
 
 	OpenGL::TempDebugGroup debuggroup("setRenderTargets");
 
-	flushStreamDraws();
+	flushBatchedDraws();
 	endPass();
 
 	bool iswindow = rts.getFirstTarget().texture == nullptr;
@@ -621,7 +621,7 @@ void Graphics::endPass()
 void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
 {
 	if (c.hasValue || stencil.hasValue || depth.hasValue)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	GLbitfield flags = 0;
 
@@ -678,7 +678,7 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors, OptionalInt sten
 		return;
 	}
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	bool drawbuffersmodified = false;
 	ncolors = std::min(ncolors, ncolorRTs);
@@ -754,7 +754,7 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors, OptionalInt sten
 
 void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
 {
-	flushStreamDraws();
+	flushBatchedDraws();
 	discard(OpenGL::FRAMEBUFFER_ALL, colorbuffers, depthstencil);
 }
 
@@ -935,7 +935,7 @@ void Graphics::present(void *screenshotCallbackData)
 
 	deprecations.draw(this);
 
-	flushStreamDraws();
+	flushBatchedDraws();
 	endPass();
 
 	gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
@@ -1038,9 +1038,9 @@ void Graphics::present(void *screenshotCallbackData)
 	glBindRenderbuffer(GL_RENDERBUFFER, info.info.uikit.colorbuffer);
 #endif
 
-	for (StreamBuffer *buffer : streamBufferState.vb)
+	for (StreamBuffer *buffer : batchedDrawState.vb)
 		buffer->nextFrame();
-	streamBufferState.indexBuffer->nextFrame();
+	batchedDrawState.indexBuffer->nextFrame();
 
 	auto window = getInstance<love::window::Window>(M_WINDOW);
 	if (window != nullptr)
@@ -1068,7 +1068,7 @@ void Graphics::present(void *screenshotCallbackData)
 
 void Graphics::setScissor(const Rect &rect)
 {
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	DisplayState &state = states.back();
 
@@ -1093,7 +1093,7 @@ void Graphics::setScissor(const Rect &rect)
 void Graphics::setScissor()
 {
 	if (states.back().scissor)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	states.back().scissor = false;
 
@@ -1111,7 +1111,7 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
 	else if (isRenderTargetActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dstexture == nullptr || !isPixelFormatStencil(dstexture->getPixelFormat())))
 		throw love::Exception("Drawing to the stencil buffer with a render target active requires either stencil=true or a custom stencil-type texture to be used, in setRenderTarget.");
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	writingToStencil = true;
 
@@ -1156,7 +1156,7 @@ void Graphics::stopDrawToStencilBuffer()
 	if (!writingToStencil)
 		return;
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	writingToStencil = false;
 
@@ -1174,7 +1174,7 @@ void Graphics::setStencilTest(CompareMode compare, int value)
 	DisplayState &state = states.back();
 
 	if (state.stencilCompare != compare || state.stencilTestValue != value)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	state.stencilCompare = compare;
 	state.stencilTestValue = value;
@@ -1211,7 +1211,7 @@ void Graphics::setDepthMode(CompareMode compare, bool write)
 	DisplayState &state = states.back();
 
 	if (state.depthTest != compare || state.depthWrite != write)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	state.depthTest = compare;
 	state.depthWrite = write;
@@ -1233,7 +1233,7 @@ void Graphics::setFrontFaceWinding(Winding winding)
 	DisplayState &state = states.back();
 
 	if (state.winding != winding)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	state.winding = winding;
 
@@ -1255,7 +1255,7 @@ void Graphics::setColor(Colorf c)
 
 void Graphics::setColorMask(ColorChannelMask mask)
 {
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	glColorMask(mask.r, mask.g, mask.b, mask.a);
 	states.back().colorMask = mask;
@@ -1264,7 +1264,7 @@ void Graphics::setColorMask(ColorChannelMask mask)
 void Graphics::setBlendState(const BlendState &blend)
 {
 	if (!(blend == states.back().blend))
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	if (blend.operationRGB == BLENDOP_MAX || blend.operationA == BLENDOP_MAX
 		|| blend.operationRGB == BLENDOP_MIN || blend.operationA == BLENDOP_MIN)
@@ -1294,8 +1294,8 @@ void Graphics::setBlendState(const BlendState &blend)
 
 void Graphics::setPointSize(float size)
 {
-	if (streamBufferState.primitiveMode == PRIMITIVE_POINTS)
-		flushStreamDraws();
+	if (batchedDrawState.primitiveMode == PRIMITIVE_POINTS)
+		flushBatchedDraws();
 
 	gl.setPointSize(size * getCurrentDPIScale());
 	states.back().pointSize = size;
@@ -1307,7 +1307,7 @@ void Graphics::setWireframe(bool enable)
 	if (GLAD_ES_VERSION_2_0)
 		return;
 
-	flushStreamDraws();
+	flushBatchedDraws();
 
 	glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
 	states.back().wireframe = enable;

+ 5 - 5
src/modules/graphics/opengl/Shader.cpp

@@ -429,7 +429,7 @@ void Shader::attach()
 {
 	if (current != this)
 	{
-		Graphics::flushStreamDrawsGlobal();
+		Graphics::flushBatchedDrawsGlobal();
 
 		gl.useProgram(program);
 		current = this;
@@ -480,7 +480,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
 	}
 
 	if (!internalupdate)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	int location = info->location;
 	UniformType type = info->baseType;
@@ -578,7 +578,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 	bool shaderactive = current == this;
 
 	if (!internalUpdate && shaderactive)
-		flushStreamDraws();
+		flushBatchedDraws();
 
 	count = std::min(count, info->count);
 
@@ -644,10 +644,10 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 	}
 }
 
-void Shader::flushStreamDraws() const
+void Shader::flushBatchedDraws() const
 {
 	if (current == this)
-		Graphics::flushStreamDrawsGlobal();
+		Graphics::flushBatchedDrawsGlobal();
 }
 
 bool Shader::hasUniform(const std::string &name) const

+ 1 - 1
src/modules/graphics/opengl/Shader.h

@@ -90,7 +90,7 @@ private:
 	TextureType getUniformTextureType(GLenum type) const;
 	bool isDepthTextureType(GLenum type) const;
 
-	void flushStreamDraws() const;
+	void flushBatchedDraws() const;
 
 	// Get any warnings or errors generated only by the shader program object.
 	std::string getProgramWarnings() const;

+ 1 - 1
src/modules/graphics/wrap_Graphics.cpp

@@ -3052,7 +3052,7 @@ int w_polygon(lua_State *L)
 
 int w_flushBatch(lua_State *)
 {
-	instance()->flushStreamDraws();
+	instance()->flushBatchedDraws();
 	return 0;
 }
 

+ 19 - 9
src/modules/graphics/wrap_Shader.cpp

@@ -411,14 +411,18 @@ int w_Shader_send(lua_State *L)
 
 	const Shader::UniformInfo *info = shader->getUniformInfo(name);
 	if (info == nullptr)
-		return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name);
-
-	int startidx = 3;
+	{
+		luax_pushboolean(L, false);
+		return 1;
+	}
 
-	if (luax_istype(L, startidx, Data::type))
-		return w_Shader_sendData(L, startidx, shader, info, false);
+	if (luax_istype(L, 3, Data::type))
+		w_Shader_sendData(L, 3, shader, info, false);
 	else
-		return w_Shader_sendLuaValues(L, startidx, shader, info, name);
+		w_Shader_sendLuaValues(L, 3, shader, info, name);
+
+	luax_pushboolean(L, true);
+	return 1;
 }
 
 int w_Shader_sendColors(lua_State *L)
@@ -428,15 +432,21 @@ int w_Shader_sendColors(lua_State *L)
 
 	const Shader::UniformInfo *info = shader->getUniformInfo(name);
 	if (info == nullptr)
-		return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name);
+	{
+		luax_pushboolean(L, false);
+		return 1;
+	}
 
 	if (info->baseType != Shader::UNIFORM_FLOAT || info->components < 3)
 		return luaL_error(L, "sendColor can only be used on vec3 or vec4 uniforms.");
 
 	if (luax_istype(L, 3, Data::type))
-		return w_Shader_sendData(L, 3, shader, info, true);
+		w_Shader_sendData(L, 3, shader, info, true);
 	else
-		return w_Shader_sendFloats(L, 3, shader, info, true);
+		w_Shader_sendFloats(L, 3, shader, info, true);
+
+	luax_pushboolean(L, true);
+	return 1;
 }
 
 int w_Shader_hasUniform(lua_State *L)

+ 2 - 5
src/modules/thread/Channel.h

@@ -36,9 +36,6 @@ namespace thread
 
 class Channel : public love::Object
 {
-// FOR WRAPPER USE ONLY
-friend int w_Channel_performAtomic(lua_State *);
-
 public:
 
 	static love::Type type;
@@ -57,11 +54,11 @@ public:
 	bool hasRead(uint64 id) const;
 	void clear();
 
-private:
-
 	void lockMutex();
 	void unlockMutex();
 
+private:
+
 	MutexRef mutex;
 	ConditionalRef cond;
 	std::queue<Variant> queue;

+ 2 - 1
src/modules/thread/LuaThread.cpp

@@ -21,6 +21,7 @@
 #include "LuaThread.h"
 #include "event/Event.h"
 #include "common/config.h"
+#include "common/runtime.h"
 
 #ifdef LOVE_BUILD_STANDALONE
 extern "C" int luaopen_love(lua_State * L);
@@ -76,7 +77,7 @@ void LuaThread::threadFunction()
 		int pushedargs = (int) args.size();
 
 		for (int i = 0; i < pushedargs; i++)
-			args[i].toLua(L);
+			luax_pushvariant(L, args[i]);
 
 		args.clear();
 

+ 5 - 5
src/modules/thread/wrap_Channel.cpp

@@ -34,7 +34,7 @@ int w_Channel_push(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
 	luax_catchexcept(L, [&]() {
-		Variant var = Variant::fromLua(L, 2);
+		Variant var = luax_checkvariant(L, 2);
 		if (var.getType() == Variant::UNKNOWN)
 			luaL_argerror(L, 2, "boolean, number, string, love type, or table expected");
 		uint64 id = c->push(var);
@@ -48,7 +48,7 @@ int w_Channel_supply(lua_State *L)
 	Channel *c = luax_checkchannel(L, 1);
 	bool result = false;
 	luax_catchexcept(L, [&]() {
-		Variant var = Variant::fromLua(L, 2);
+		Variant var = luax_checkvariant(L, 2);
 		if (var.getType() == Variant::UNKNOWN)
 			luaL_argerror(L, 2, "boolean, number, string, love type, or table expected");
 		if (lua_isnumber(L, 3))
@@ -66,7 +66,7 @@ int w_Channel_pop(lua_State *L)
 	Channel *c = luax_checkchannel(L, 1);
 	Variant var;
 	if (c->pop(&var))
-		var.toLua(L);
+		luax_pushvariant(L, var);
 	else
 		lua_pushnil(L);
 	return 1;
@@ -84,7 +84,7 @@ int w_Channel_demand(lua_State *L)
 		result = c->demand(&var);
 
 	if (result)
-		var.toLua(L);
+		luax_pushvariant(L, var);
 	else
 		lua_pushnil(L);
 	return 1;
@@ -95,7 +95,7 @@ int w_Channel_peek(lua_State *L)
 	Channel *c = luax_checkchannel(L, 1);
 	Variant var;
 	if (c->peek(&var))
-		var.toLua(L);
+		luax_pushvariant(L, var);
 	else
 		lua_pushnil(L);
 	return 1;

+ 1 - 0
src/modules/thread/wrap_Channel.h

@@ -23,6 +23,7 @@
 
 // LOVE
 #include "Channel.h"
+#include "common/runtime.h"
 
 namespace love
 {

+ 1 - 1
src/modules/thread/wrap_LuaThread.cpp

@@ -39,7 +39,7 @@ int w_Thread_start(lua_State *L)
 	for (int i = 0; i < nargs; ++i)
 	{
 		luax_catchexcept(L, [&]() {
-			args.push_back(Variant::fromLua(L, i+2));
+			args.push_back(luax_checkvariant(L, i+2));
 		});
 
 		if (args.back().getType() == Variant::UNKNOWN)

+ 1 - 0
src/modules/thread/wrap_LuaThread.h

@@ -23,6 +23,7 @@
 
 // LOVE
 #include "ThreadModule.h"
+#include "common/runtime.h"
 
 namespace love
 {