Browse Source

graphics: handle default textures in high level code.

Previously each backend had to set up default textures with its own code, which needs some care for it to be robust.
Sasha Szpakowski 1 year ago
parent
commit
8872497561

+ 93 - 6
src/modules/graphics/Graphics.cpp

@@ -195,6 +195,7 @@ Graphics::Graphics()
 	, quadIndexBuffer(nullptr)
 	, quadIndexBuffer(nullptr)
 	, fanIndexBuffer(nullptr)
 	, fanIndexBuffer(nullptr)
 	, capabilities()
 	, capabilities()
+	, defaultTextures()
 	, cachedShaderStages()
 	, cachedShaderStages()
 {
 {
 	transformStack.reserve(16);
 	transformStack.reserve(16);
@@ -217,6 +218,8 @@ Graphics::~Graphics()
 	if (fanIndexBuffer != nullptr)
 	if (fanIndexBuffer != nullptr)
 		fanIndexBuffer->release();
 		fanIndexBuffer->release();
 
 
+	releaseDefaultResources();
+
 	// Clean up standard shaders before the active shader. If we do it after,
 	// Clean up standard shaders before the active shader. If we do it after,
 	// the active shader may try to activate a standard shader when deactivating
 	// the active shader may try to activate a standard shader when deactivating
 	// itself, which will cause problems since it calls Graphics methods in the
 	// itself, which will cause problems since it calls Graphics methods in the
@@ -530,6 +533,90 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
 	return Shader::validate(stages, err);
 	return Shader::validate(stages, err);
 }
 }
 
 
+Texture *Graphics::getDefaultTexture(TextureType type, DataBaseType dataType)
+{
+	Texture *tex = defaultTextures[type][dataType];
+	if (tex != nullptr)
+		return tex;
+
+	Texture::Settings settings;
+	settings.type = type;
+
+	switch (dataType)
+	{
+	case DATA_BASETYPE_INT:
+		settings.format = PIXELFORMAT_RGBA8_INT;
+		break;
+	case DATA_BASETYPE_UINT:
+		settings.format = PIXELFORMAT_RGBA8_UINT;
+		break;
+	case DATA_BASETYPE_FLOAT:
+	default:
+		settings.format = PIXELFORMAT_RGBA8_UNORM;
+		break;
+	}
+
+	std::string name = "default_";
+
+	const char *tname = "unknown";
+	Texture::getConstant(type, tname);
+	name += tname;
+
+	const char *formatname = "unknown";
+	love::getConstant(settings.format, formatname);
+	name += std::string("_") + formatname;
+
+	settings.debugName = name;
+
+	tex = newTexture(settings);
+
+	SamplerState s;
+	s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
+	s.wrapU = s.wrapV = s.wrapW = SamplerState::WRAP_CLAMP;
+	tex->setSamplerState(s);
+
+	uint8 pixel[] = {255, 255, 255, 255};
+	if (isPixelFormatInteger(settings.format))
+		pixel[0] = pixel[1] = pixel[2] = pixel[3] = 1;
+
+	for (int slice = 0; slice < (type == TEXTURE_CUBE ? 6 : 1); slice++)
+		tex->replacePixels(pixel, sizeof(pixel), slice, 0, {0, 0, 1, 1}, false);
+
+	defaultTextures[type][dataType] = tex;
+
+	return tex;
+}
+
+void Graphics::releaseDefaultResources()
+{
+	for (int type = 0; type < TEXTURE_MAX_ENUM; type++)
+	{
+		for (int dataType = 0; dataType < DATA_BASETYPE_MAX_ENUM; dataType++)
+		{
+			if (defaultTextures[type][dataType])
+				defaultTextures[type][dataType]->release();
+			defaultTextures[type][dataType] = nullptr;
+		}
+	}
+}
+
+Texture *Graphics::getTextureOrDefaultForActiveShader(Texture *tex)
+{
+	if (tex != nullptr)
+		return tex;
+
+	Shader *shader = Shader::current;
+
+	if (shader != nullptr)
+	{
+		auto texinfo = shader->getMainTextureInfo();
+		if (texinfo != nullptr && texinfo->textureType != TEXTURE_MAX_ENUM)
+			return getDefaultTexture(texinfo->textureType, texinfo->dataBaseType);
+	}
+
+	return getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT);
+}
+
 int Graphics::getWidth() const
 int Graphics::getWidth() const
 {
 {
 	return width;
 	return width;
@@ -1825,7 +1912,7 @@ void Graphics::flushBatchedDraws()
 		cmd.indexCount = sbstate.indexCount;
 		cmd.indexCount = sbstate.indexCount;
 		cmd.indexType = INDEX_UINT16;
 		cmd.indexType = INDEX_UINT16;
 		cmd.indexBufferOffset = sbstate.indexBuffer->unmap(usedsizes[2]);
 		cmd.indexBufferOffset = sbstate.indexBuffer->unmap(usedsizes[2]);
-		cmd.texture = sbstate.texture;
+		cmd.texture = getTextureOrDefaultForActiveShader(sbstate.texture);
 		draw(cmd);
 		draw(cmd);
 
 
 		sbstate.indexBufferMap = StreamBuffer::MapInfo();
 		sbstate.indexBufferMap = StreamBuffer::MapInfo();
@@ -1836,7 +1923,7 @@ void Graphics::flushBatchedDraws()
 		cmd.primitiveType = sbstate.primitiveMode;
 		cmd.primitiveType = sbstate.primitiveMode;
 		cmd.vertexStart = 0;
 		cmd.vertexStart = 0;
 		cmd.vertexCount = sbstate.vertexCount;
 		cmd.vertexCount = sbstate.vertexCount;
-		cmd.texture = sbstate.texture;
+		cmd.texture = getTextureOrDefaultForActiveShader(sbstate.texture);
 		draw(cmd);
 		draw(cmd);
 	}
 	}
 
 
@@ -1933,7 +2020,7 @@ void Graphics::drawFromShader(PrimitiveType primtype, int vertexcount, int insta
 	cmd.primitiveType = primtype;
 	cmd.primitiveType = primtype;
 	cmd.vertexCount = vertexcount;
 	cmd.vertexCount = vertexcount;
 	cmd.instanceCount = std::max(1, instancecount);
 	cmd.instanceCount = std::max(1, instancecount);
-	cmd.texture = maintexture;
+	cmd.texture = getTextureOrDefaultForActiveShader(maintexture);
 
 
 	draw(cmd);
 	draw(cmd);
 }
 }
@@ -1974,7 +2061,7 @@ void Graphics::drawFromShader(Buffer *indexbuffer, int indexcount, int instancec
 	cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
 	cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
 	cmd.indexBufferOffset = startindex * getIndexDataSize(cmd.indexType);
 	cmd.indexBufferOffset = startindex * getIndexDataSize(cmd.indexType);
 
 
-	cmd.texture = maintexture;
+	cmd.texture = getTextureOrDefaultForActiveShader(maintexture);
 
 
 	draw(cmd);
 	draw(cmd);
 }
 }
@@ -2001,7 +2088,7 @@ void Graphics::drawFromShaderIndirect(PrimitiveType primtype, Buffer *indirectar
 	cmd.primitiveType = primtype;
 	cmd.primitiveType = primtype;
 	cmd.indirectBuffer = indirectargs;
 	cmd.indirectBuffer = indirectargs;
 	cmd.indirectBufferOffset = argsindex * indirectargs->getArrayStride();
 	cmd.indirectBufferOffset = argsindex * indirectargs->getArrayStride();
-	cmd.texture = maintexture;
+	cmd.texture = getTextureOrDefaultForActiveShader(maintexture);
 
 
 	draw(cmd);
 	draw(cmd);
 }
 }
@@ -2029,7 +2116,7 @@ void Graphics::drawFromShaderIndirect(Buffer *indexbuffer, Buffer *indirectargs,
 	cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
 	cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
 	cmd.indirectBuffer = indirectargs;
 	cmd.indirectBuffer = indirectargs;
 	cmd.indexBufferOffset = argsindex * indirectargs->getArrayStride();
 	cmd.indexBufferOffset = argsindex * indirectargs->getArrayStride();
-	cmd.texture = maintexture;
+	cmd.texture = getTextureOrDefaultForActiveShader(maintexture);
 
 
 	draw(cmd);
 	draw(cmd);
 }
 }

+ 7 - 0
src/modules/graphics/Graphics.h

@@ -487,6 +487,9 @@ public:
 
 
 	bool validateShader(bool gles, const std::vector<std::string> &stages, const Shader::CompileOptions &options, std::string &err);
 	bool validateShader(bool gles, const std::vector<std::string> &stages, const Shader::CompileOptions &options, std::string &err);
 
 
+	Texture *getDefaultTexture(TextureType type, DataBaseType dataType);
+	Texture *getTextureOrDefaultForActiveShader(Texture *tex);
+
 	/**
 	/**
 	 * Resets the current color, background color, line style, and so forth.
 	 * Resets the current color, background color, line style, and so forth.
 	 **/
 	 **/
@@ -1039,6 +1042,8 @@ protected:
 
 
 	void updatePendingReadbacks();
 	void updatePendingReadbacks();
 
 
+	void releaseDefaultResources();
+
 	void restoreState(const DisplayState &s);
 	void restoreState(const DisplayState &s);
 	void restoreStateChecked(const DisplayState &s);
 	void restoreStateChecked(const DisplayState &s);
 
 
@@ -1094,6 +1099,8 @@ private:
 	void checkSetDefaultFont();
 	void checkSetDefaultFont();
 	int calculateEllipsePoints(float rx, float ry) const;
 	int calculateEllipsePoints(float rx, float ry) const;
 
 
+	Texture *defaultTextures[TEXTURE_MAX_ENUM][DATA_BASETYPE_MAX_ENUM];
+
 	std::vector<uint8> scratchBuffer;
 	std::vector<uint8> scratchBuffer;
 
 
 	std::unordered_map<std::string, ShaderStage *> cachedShaderStages[SHADERSTAGE_MAX_ENUM];
 	std::unordered_map<std::string, ShaderStage *> cachedShaderStages[SHADERSTAGE_MAX_ENUM];

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

@@ -667,7 +667,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
 		cmd.primitiveType = primitiveType;
 		cmd.primitiveType = primitiveType;
 		cmd.indexType = indexDataType;
 		cmd.indexType = indexDataType;
 		cmd.instanceCount = instancecount;
 		cmd.instanceCount = instancecount;
-		cmd.texture = texture;
+		cmd.texture = gfx->getTextureOrDefaultForActiveShader(texture);
 		cmd.cullMode = gfx->getMeshCullMode();
 		cmd.cullMode = gfx->getMeshCullMode();
 
 
 		cmd.indexBufferOffset = r.getOffset() * indexbuffer->getArrayStride();
 		cmd.indexBufferOffset = r.getOffset() * indexbuffer->getArrayStride();
@@ -691,7 +691,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
 		cmd.vertexStart = (int) r.getOffset();
 		cmd.vertexStart = (int) r.getOffset();
 		cmd.vertexCount = (int) r.getSize();
 		cmd.vertexCount = (int) r.getSize();
 		cmd.instanceCount = instancecount;
 		cmd.instanceCount = instancecount;
-		cmd.texture = texture;
+		cmd.texture = gfx->getTextureOrDefaultForActiveShader(texture);
 		cmd.cullMode = gfx->getMeshCullMode();
 		cmd.cullMode = gfx->getMeshCullMode();
 
 
 		cmd.indirectBuffer = indirectargs;
 		cmd.indirectBuffer = indirectargs;

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

@@ -1086,7 +1086,8 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
 	BufferBindings vertexbuffers;
 	BufferBindings vertexbuffers;
 	vertexbuffers.set(0, buffer, 0);
 	vertexbuffers.set(0, buffer, 0);
 
 
-	gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
+	Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture);
+	gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, tex);
 }
 }
 
 
 bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
 bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)

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

@@ -395,7 +395,10 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	count = std::min(count, next - start);
 	count = std::min(count, next - start);
 
 
 	if (count > 0)
 	if (count > 0)
-		gfx->drawQuads(start, count, attributes, buffers, texture);
+	{
+		Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture);
+		gfx->drawQuads(start, count, attributes, buffers, tex);
+	}
 }
 }
 
 
 } // graphics
 } // graphics

+ 4 - 1
src/modules/graphics/TextBatch.cpp

@@ -290,7 +290,10 @@ void TextBatch::draw(Graphics *gfx, const Matrix4 &m)
 	Graphics::TempTransform transform(gfx, m);
 	Graphics::TempTransform transform(gfx, m);
 
 
 	for (const Font::DrawCommand &cmd : drawCommands)
 	for (const Font::DrawCommand &cmd : drawCommands)
-		gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
+	{
+		Texture *tex = gfx->getTextureOrDefaultForActiveShader(cmd.texture);
+		gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, tex);
+	}
 }
 }
 
 
 } // graphics
 } // graphics

+ 0 - 3
src/modules/graphics/metal/Graphics.h

@@ -139,7 +139,6 @@ public:
 
 
 	StreamBuffer *getUniformBuffer() const { return uniformBuffer; }
 	StreamBuffer *getUniformBuffer() const { return uniformBuffer; }
 	Buffer *getDefaultAttributesBuffer() const { return defaultAttributesBuffer; }
 	Buffer *getDefaultAttributesBuffer() const { return defaultAttributesBuffer; }
-	Texture *getDefaultTexture(TextureType textype) const { return defaultTextures[textype]; }
 
 
 	int getClosestMSAASamples(int requestedsamples);
 	int getClosestMSAASamples(int requestedsamples);
 
 
@@ -247,8 +246,6 @@ private:
 
 
 	Buffer *defaultAttributesBuffer;
 	Buffer *defaultAttributesBuffer;
 
 
-	Texture *defaultTextures[TEXTURE_MAX_ENUM];
-
 	std::map<uint64, void *> cachedSamplers;
 	std::map<uint64, void *> cachedSamplers;
 	std::unordered_map<uint64, void *> cachedDepthStencilStates;
 	std::unordered_map<uint64, void *> cachedDepthStencilStates;
 
 

+ 0 - 22
src/modules/graphics/metal/Graphics.mm

@@ -281,7 +281,6 @@ Graphics::Graphics()
 	, renderBindings()
 	, renderBindings()
 	, uniformBufferOffset(0)
 	, uniformBufferOffset(0)
 	, defaultAttributesBuffer(nullptr)
 	, defaultAttributesBuffer(nullptr)
-	, defaultTextures()
 	, families()
 	, families()
 { @autoreleasepool {
 { @autoreleasepool {
 	if (@available(macOS 10.15, iOS 13.0, *))
 	if (@available(macOS 10.15, iOS 13.0, *))
@@ -346,17 +345,6 @@ Graphics::Graphics()
 		defaultAttributesBuffer = newBuffer(attribsettings, dataformat, &defaults, sizeof(DefaultVertexAttributes), 0);
 		defaultAttributesBuffer = newBuffer(attribsettings, dataformat, &defaults, sizeof(DefaultVertexAttributes), 0);
 	}
 	}
 
 
-	uint8 defaultpixel[] = {255, 255, 255, 255};
-	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
-	{
-		Texture::Settings settings;
-		settings.type = (TextureType) i;
-		settings.format = PIXELFORMAT_RGBA8_UNORM;
-		defaultTextures[i] = newTexture(settings);
-		Rect r = {0, 0, 1, 1};
-		defaultTextures[i]->replacePixels(defaultpixel, sizeof(defaultpixel), 0, 0, r, false);
-	}
-
 	if (batchedDrawState.vb[0] == nullptr)
 	if (batchedDrawState.vb[0] == nullptr)
 	{
 	{
 		// Initial sizes that should be good enough for most cases. It will
 		// Initial sizes that should be good enough for most cases. It will
@@ -426,9 +414,6 @@ Graphics::~Graphics()
 	commandQueue = nil;
 	commandQueue = nil;
 	device = nil;
 	device = nil;
 
 
-	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
-		defaultTextures[i]->release();
-
 	for (auto &kvp : cachedSamplers)
 	for (auto &kvp : cachedSamplers)
 		CFBridgingRelease(kvp.second);
 		CFBridgingRelease(kvp.second);
 
 
@@ -1144,13 +1129,6 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
 
 
 		if (b.isMainTexture)
 		if (b.isMainTexture)
 		{
 		{
-			if (maintex == nullptr)
-			{
-				auto texinfo = shader->getMainTextureInfo();
-				if (texinfo != nullptr && texinfo->textureType != TEXTURE_MAX_ENUM)
-					maintex = defaultTextures[texinfo->textureType];
-			}
-
 			texture = getMTLTexture(maintex);
 			texture = getMTLTexture(maintex);
 			samplertex = maintex;
 			samplertex = maintex;
 		}
 		}

+ 10 - 3
src/modules/graphics/metal/Shader.mm

@@ -524,7 +524,7 @@ void Shader::addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Re
 
 
 	if (u.baseType == UNIFORM_SAMPLER)
 	if (u.baseType == UNIFORM_SAMPLER)
 	{
 	{
-		auto tex = Graphics::getInstance()->getDefaultTexture(u.textureType);
+		auto tex = Graphics::getInstance()->getDefaultTexture(u.textureType, u.dataBaseType);
 		for (int i = 0; i < u.count; i++)
 		for (int i = 0; i < u.count; i++)
 		{
 		{
 			tex->retain();
 			tex->retain();
@@ -538,8 +538,15 @@ void Shader::addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Re
 	}
 	}
 	else if (u.baseType == UNIFORM_STORAGETEXTURE)
 	else if (u.baseType == UNIFORM_STORAGETEXTURE)
 	{
 	{
+		Texture *tex = nullptr;
+		if ((u.access & ACCESS_WRITE) == 0)
+			tex = Graphics::getInstance()->getDefaultTexture(u.textureType, u.dataBaseType);
 		for (int i = 0; i < u.count; i++)
 		for (int i = 0; i < u.count; i++)
-			u.textures[i] = nullptr;
+		{
+			if (tex)
+				tex->retain();
+			u.textures[i] = tex;
+		}
 	}
 	}
 
 
 	uniforms[u.name] = u;
 	uniforms[u.name] = u;
@@ -983,7 +990,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 		else
 		else
 		{
 		{
 			auto gfx = Graphics::getInstance();
 			auto gfx = Graphics::getInstance();
-			tex = gfx->getDefaultTexture(info->textureType);
+			tex = gfx->getDefaultTexture(info->textureType, info->dataBaseType);
 		}
 		}
 
 
 		tex->retain();
 		tex->retain();

+ 4 - 1
src/modules/graphics/opengl/Graphics.cpp

@@ -1765,7 +1765,10 @@ uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable)
 		// Make sure at least something is bound to a color attachment. I believe
 		// Make sure at least something is bound to a color attachment. I believe
 		// this is required on ES2 but I'm not positive.
 		// this is required on ES2 but I'm not positive.
 		if (isPixelFormatDepthStencil(format))
 		if (isPixelFormatDepthStencil(format))
-			gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0);
+		{
+			love::graphics::Texture *tex = getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT);
+			gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, (GLuint) tex->getHandle(), 0, 0, 0);
+		}
 
 
 		if (readable)
 		if (readable)
 		{
 		{

+ 5 - 109
src/modules/graphics/opengl/OpenGL.cpp

@@ -323,8 +323,6 @@ void OpenGL::setupContext()
 	setStencilWriteMask(state.stencilWriteMask);
 	setStencilWriteMask(state.stencilWriteMask);
 	setColorWriteMask(state.colorWriteMask);
 	setColorWriteMask(state.colorWriteMask);
 
 
-	createDefaultTexture();
-
 	contextInitialized = true;
 	contextInitialized = true;
 
 
 #ifdef LOVE_ANDROID
 #ifdef LOVE_ANDROID
@@ -341,18 +339,6 @@ void OpenGL::deInitContext()
 	if (!contextInitialized)
 	if (!contextInitialized)
 		return;
 		return;
 
 
-	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
-	{
-		for (int datatype = DATA_BASETYPE_FLOAT; datatype <= DATA_BASETYPE_UINT; datatype++)
-		{
-			if (state.defaultTexture[i][datatype] != 0)
-			{
-				gl.deleteTexture(state.defaultTexture[i][datatype]);
-				state.defaultTexture[i][datatype] = 0;
-			}
-		}
-	}
-
 	contextInitialized = false;
 	contextInitialized = false;
 }
 }
 
 
@@ -604,71 +590,6 @@ void OpenGL::initMaxValues()
 		maxLODBias = 0.0f;
 		maxLODBias = 0.0f;
 }
 }
 
 
-void OpenGL::createDefaultTexture()
-{
-	// Set the 'default' texture as a repeating white pixel. Otherwise, texture
-	// calls inside a shader would return black when drawing graphics primitives
-	// which would create the need to use different "passthrough" shaders for
-	// untextured primitives vs images.
-	const GLubyte pix[] = {255, 255, 255, 255};
-	const GLubyte intpix[] = {1, 1, 1, 1};
-
-	SamplerState s;
-	s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
-	s.wrapU = s.wrapV = s.wrapW = SamplerState::WRAP_CLAMP;
-
-	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
-	{
-		for (int datatype = (int)DATA_BASETYPE_FLOAT; datatype <= (int)DATA_BASETYPE_UINT; datatype++)
-		{
-			state.defaultTexture[i][datatype] = 0;
-
-			TextureType type = (TextureType) i;
-
-			if (!isTextureTypeSupported(type))
-				continue;
-
-			if (datatype != DATA_BASETYPE_FLOAT && !(GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0))
-				continue;
-
-			GLuint curtexture = state.boundTextures[type][0];
-
-			glGenTextures(1, &state.defaultTexture[type][datatype]);
-			bindTextureToUnit(type, state.defaultTexture[type][datatype], 0, false);
-
-			setSamplerState(type, s);
-
-			PixelFormat format = PIXELFORMAT_RGBA8_UNORM;
-			if (datatype == DATA_BASETYPE_INT)
-				format = PIXELFORMAT_RGBA8_INT;
-			else if (datatype == DATA_BASETYPE_UINT)
-				format = PIXELFORMAT_RGBA8_UINT;
-
-			const GLubyte *p = datatype == DATA_BASETYPE_FLOAT ? pix : intpix;
-
-			rawTexStorage(type, 1, format, 1, 1);
-
-			TextureFormat fmt = convertPixelFormat(format, false);
-			int slices = type == TEXTURE_CUBE ? 6 : 1;
-
-			for (int slice = 0; slice < slices; slice++)
-			{
-				GLenum gltarget = getGLTextureType(type);
-
-				if (type == TEXTURE_CUBE)
-					gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
-
-				if (type == TEXTURE_2D || type == TEXTURE_CUBE)
-					glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, p);
-				else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
-					glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, p);
-			}
-
-			bindTextureToUnit(type, curtexture, 0, false);
-		}
-	}
-}
-
 void OpenGL::prepareDraw(love::graphics::Graphics *gfx)
 void OpenGL::prepareDraw(love::graphics::Graphics *gfx)
 {
 {
 	TempDebugGroup debuggroup("Prepare OpenGL draw");
 	TempDebugGroup debuggroup("Prepare OpenGL draw");
@@ -702,6 +623,7 @@ GLenum OpenGL::getGLBufferType(BufferUsage usage)
 		case BUFFERUSAGE_VERTEX: return GL_ARRAY_BUFFER;
 		case BUFFERUSAGE_VERTEX: return GL_ARRAY_BUFFER;
 		case BUFFERUSAGE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
 		case BUFFERUSAGE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
 		case BUFFERUSAGE_TEXEL: return GL_TEXTURE_BUFFER;
 		case BUFFERUSAGE_TEXEL: return GL_TEXTURE_BUFFER;
+		case BUFFERUSAGE_UNIFORM: return GL_UNIFORM_BUFFER;
 		case BUFFERUSAGE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
 		case BUFFERUSAGE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
 		case BUFFERUSAGE_INDIRECT_ARGUMENTS: return GL_DRAW_INDIRECT_BUFFER;
 		case BUFFERUSAGE_INDIRECT_ARGUMENTS: return GL_DRAW_INDIRECT_BUFFER;
 		case BUFFERUSAGE_MAX_ENUM: return GL_ZERO;
 		case BUFFERUSAGE_MAX_ENUM: return GL_ZERO;
@@ -1191,11 +1113,6 @@ GLuint OpenGL::getDefaultFBO() const
 #endif
 #endif
 }
 }
 
 
-GLuint OpenGL::getDefaultTexture(TextureType type, DataBaseType datatype) const
-{
-	return state.defaultTexture[type][datatype];
-}
-
 void OpenGL::setTextureUnit(int textureunit)
 void OpenGL::setTextureUnit(int textureunit)
 {
 {
 	if (textureunit != state.curTextureUnit)
 	if (textureunit != state.curTextureUnit)
@@ -1234,31 +1151,8 @@ void OpenGL::bindBufferTextureToUnit(GLuint texture, int textureunit, bool resto
 
 
 void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
 void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
 {
 {
-	TextureType textype = TEXTURE_2D;
-	GLuint handle = 0;
-
-	if (texture != nullptr)
-	{
-		textype = texture->getTextureType();
-		handle = (GLuint) texture->getHandle();
-	}
-	else
-	{
-		DataBaseType datatype = DATA_BASETYPE_FLOAT;
-
-		if (textureunit == 0 && Shader::current != nullptr)
-		{
-			const Shader::UniformInfo *info = Shader::current->getMainTextureInfo();
-			if (info != nullptr)
-			{
-				textype = info->textureType;
-				datatype = info->dataBaseType;
-			}
-		}
-
-		handle = getDefaultTexture(textype, datatype);
-	}
-
+	TextureType textype = texture->getTextureType();
+	GLuint handle = (GLuint) texture->getHandle();
 	bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
 	bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
 }
 }
 
 
@@ -1537,6 +1431,8 @@ bool OpenGL::isBufferUsageSupported(BufferUsage usage) const
 		return true;
 		return true;
 	case BUFFERUSAGE_TEXEL:
 	case BUFFERUSAGE_TEXEL:
 		return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_2;
 		return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_2;
+	case BUFFERUSAGE_UNIFORM:
+		return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_0;
 	case BUFFERUSAGE_SHADER_STORAGE:
 	case BUFFERUSAGE_SHADER_STORAGE:
 		return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
 		return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
 	case BUFFERUSAGE_INDIRECT_ARGUMENTS:
 	case BUFFERUSAGE_INDIRECT_ARGUMENTS:

+ 0 - 7
src/modules/graphics/opengl/OpenGL.h

@@ -330,11 +330,6 @@ public:
 	 **/
 	 **/
 	GLuint getDefaultFBO() const;
 	GLuint getDefaultFBO() const;
 
 
-	/**
-	 * Gets the ID for love's default texture (used for "untextured" primitives.)
-	 **/
-	GLuint getDefaultTexture(TextureType type, DataBaseType datatype) const;
-
 	/**
 	/**
 	 * Gets the texture ID for love's default texel buffer.
 	 * Gets the texture ID for love's default texel buffer.
 	 **/
 	 **/
@@ -492,7 +487,6 @@ private:
 	void initVendor();
 	void initVendor();
 	void initOpenGLFunctions();
 	void initOpenGLFunctions();
 	void initMaxValues();
 	void initMaxValues();
-	void createDefaultTexture();
 
 
 	bool contextInitialized;
 	bool contextInitialized;
 
 
@@ -550,7 +544,6 @@ private:
 
 
 		GLuint boundFramebuffers[2];
 		GLuint boundFramebuffers[2];
 
 
-		GLuint defaultTexture[TEXTURE_MAX_ENUM][DATA_BASETYPE_MAX_ENUM];
 		GLuint defaultTexelBuffer;
 		GLuint defaultTexelBuffer;
 		GLuint defaultStorageBuffer;
 		GLuint defaultStorageBuffer;
 
 

+ 35 - 16
src/modules/graphics/opengl/Shader.cpp

@@ -110,6 +110,8 @@ void Shader::mapActiveUniforms()
 	std::map<std::string, UniformInfo> olduniforms = uniforms;
 	std::map<std::string, UniformInfo> olduniforms = uniforms;
 	uniforms.clear();
 	uniforms.clear();
 
 
+	auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
+
 	for (int uindex = 0; uindex < numuniforms; uindex++)
 	for (int uindex = 0; uindex < numuniforms; uindex++)
 	{
 	{
 		GLsizei namelen = 0;
 		GLsizei namelen = 0;
@@ -140,7 +142,7 @@ void Shader::mapActiveUniforms()
 			continue;
 			continue;
 
 
 		if (!fillUniformReflectionData(u))
 		if (!fillUniformReflectionData(u))
-			continue;;
+			continue;
 
 
 		if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
 		if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
 		{
 		{
@@ -156,7 +158,7 @@ void Shader::mapActiveUniforms()
 			else
 			else
 			{
 			{
 				unit.isTexelBuffer = false;
 				unit.isTexelBuffer = false;
-				unit.texture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
+				unit.texture = 0; // Handled below.
 			}
 			}
 
 
 			for (int i = 0; i < u.count; i++)
 			for (int i = 0; i < u.count; i++)
@@ -165,7 +167,7 @@ void Shader::mapActiveUniforms()
 		else if (u.baseType == UNIFORM_STORAGETEXTURE)
 		else if (u.baseType == UNIFORM_STORAGETEXTURE)
 		{
 		{
 			StorageTextureBinding binding = {};
 			StorageTextureBinding binding = {};
-			binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
+			binding.gltexture = 0; // Handled below.
 			binding.type = u.textureType;
 			binding.type = u.textureType;
 
 
 			if ((u.access & (ACCESS_READ | ACCESS_WRITE)) != 0)
 			if ((u.access & (ACCESS_READ | ACCESS_WRITE)) != 0)
@@ -247,7 +249,13 @@ void Shader::mapActiveUniforms()
 					else
 					else
 					{
 					{
 						u.textures = new love::graphics::Texture*[u.count];
 						u.textures = new love::graphics::Texture*[u.count];
-						memset(u.textures, 0, sizeof(Texture *) * u.count);
+
+						auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType);
+						for (int i = 0; i < u.count; i++)
+						{
+							tex->retain();
+							u.textures[i] = tex;
+						}
 					}
 					}
 				}
 				}
 				else if (u.baseType == UNIFORM_STORAGETEXTURE)
 				else if (u.baseType == UNIFORM_STORAGETEXTURE)
@@ -259,7 +267,20 @@ void Shader::mapActiveUniforms()
 					glUniform1iv(u.location, u.count, u.ints);
 					glUniform1iv(u.location, u.count, u.ints);
 
 
 					u.textures = new love::graphics::Texture*[u.count];
 					u.textures = new love::graphics::Texture*[u.count];
-					memset(u.textures, 0, sizeof(Texture *) * u.count);
+
+					if ((u.access & ACCESS_WRITE) != 0)
+					{
+						memset(u.textures, 0, sizeof(Texture *) * u.count);
+					}
+					else
+					{
+						auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType);
+						for (int i = 0; i < u.count; i++)
+						{
+							tex->retain();
+							u.textures[i] = tex;
+						}
+					}
 				}
 				}
 			}
 			}
 
 
@@ -777,9 +798,15 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 		{
 		{
 			if (!validateTexture(info, tex, internalUpdate))
 			if (!validateTexture(info, tex, internalUpdate))
 				continue;
 				continue;
-			tex->retain();
+		}
+		else
+		{
+			auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
+			tex = gfx->getDefaultTexture(info->textureType, info->dataBaseType);
 		}
 		}
 
 
+		tex->retain();
+
 		if (info->textures[i] != nullptr)
 		if (info->textures[i] != nullptr)
 			info->textures[i]->release();
 			info->textures[i]->release();
 
 
@@ -787,11 +814,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 
 
 		if (isstoragetex)
 		if (isstoragetex)
 		{
 		{
-			GLuint gltex = 0;
-			if (tex != nullptr)
-				gltex = (GLuint) tex->getHandle();
-			else
-				gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
+			GLuint gltex = (GLuint) tex->getHandle();
 
 
 			int bindingindex = info->ints[i];
 			int bindingindex = info->ints[i];
 			auto &binding = storageTextureBindings[bindingindex];
 			auto &binding = storageTextureBindings[bindingindex];
@@ -804,11 +827,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 		}
 		}
 		else
 		else
 		{
 		{
-			GLuint gltex = 0;
-			if (textures[i] != nullptr)
-				gltex = (GLuint) tex->getHandle();
-			else
-				gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
+			GLuint gltex = (GLuint) tex->getHandle();
 
 
 			int texunit = info->ints[i];
 			int texunit = info->ints[i];
 
 

+ 1 - 15
src/modules/graphics/vulkan/Graphics.cpp

@@ -157,7 +157,6 @@ Graphics::~Graphics()
 {
 {
 	defaultConstantTexCoord.set(nullptr);
 	defaultConstantTexCoord.set(nullptr);
 	defaultConstantColor.set(nullptr);
 	defaultConstantColor.set(nullptr);
-	defaultTexture.set(nullptr);
 
 
 	Volatile::unloadAll();
 	Volatile::unloadAll();
 	cleanup();
 	cleanup();
@@ -676,7 +675,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 			defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
 			defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
 		}
 		}
 
 
-		createDefaultTexture();
 		createDefaultShaders();
 		createDefaultShaders();
 		Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT];
 		Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT];
 		createQuadIndexBuffer();
 		createQuadIndexBuffer();
@@ -2384,10 +2382,7 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
 		offsets.push_back((VkDeviceSize)0);
 		offsets.push_back((VkDeviceSize)0);
 	}
 	}
 
 
-	if (texture == nullptr)
-		configuration.shader->setMainTex(defaultTexture);
-	else
-		configuration.shader->setMainTex(texture);
+	configuration.shader->setMainTex(texture);
 
 
 	ensureGraphicsPipelineConfiguration(configuration);
 	ensureGraphicsPipelineConfiguration(configuration);
 
 
@@ -3035,15 +3030,6 @@ void Graphics::createSyncObjects()
 			throw love::Exception("failed to create synchronization objects for a frame!");
 			throw love::Exception("failed to create synchronization objects for a frame!");
 }
 }
 
 
-void Graphics::createDefaultTexture()
-{
-	Texture::Settings settings;
-	defaultTexture.set(newTexture(settings, nullptr), Acquire::NORETAIN);
-
-	uint8_t whitePixels[] = {255, 255, 255, 255};
-	defaultTexture->replacePixels(whitePixels, sizeof(whitePixels), 0, 0, { 0, 0, 1, 1 }, false);
-}
-
 void Graphics::cleanup()
 void Graphics::cleanup()
 {
 {
 	for (auto &cleanUpFns : cleanUpFunctions)
 	for (auto &cleanUpFns : cleanUpFunctions)

+ 0 - 2
src/modules/graphics/vulkan/Graphics.h

@@ -367,7 +367,6 @@ private:
 	void createCommandPool();
 	void createCommandPool();
 	void createCommandBuffers();
 	void createCommandBuffers();
 	void createSyncObjects();
 	void createSyncObjects();
-	void createDefaultTexture();
 	void cleanup();
 	void cleanup();
 	void cleanupSwapChain();
 	void cleanupSwapChain();
 	void recreateSwapChain();
 	void recreateSwapChain();
@@ -442,7 +441,6 @@ private:
 	bool swapChainRecreationRequested = false;
 	bool swapChainRecreationRequested = false;
 	bool transitionColorDepthLayouts = false;
 	bool transitionColorDepthLayouts = false;
 	VmaAllocator vmaAllocator = VK_NULL_HANDLE;
 	VmaAllocator vmaAllocator = VK_NULL_HANDLE;
-	StrongRef<love::graphics::Texture> defaultTexture;
 	StrongRef<love::graphics::Buffer> defaultConstantColor;
 	StrongRef<love::graphics::Buffer> defaultConstantColor;
 	StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
 	StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
 	// functions that need to be called to cleanup objects that were needed for rendering a frame.
 	// functions that need to be called to cleanup objects that were needed for rendering a frame.