|
@@ -18,11 +18,14 @@
|
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
|
**/
|
|
|
|
|
|
-#include "Canvas.h"
|
|
|
+#include "Texture.h"
|
|
|
+
|
|
|
#include "graphics/Graphics.h"
|
|
|
#include "Graphics.h"
|
|
|
+#include "common/int.h"
|
|
|
|
|
|
-#include <algorithm> // For min/max
|
|
|
+// STD
|
|
|
+#include <algorithm> // for min/max
|
|
|
|
|
|
namespace love
|
|
|
{
|
|
@@ -31,7 +34,7 @@ namespace graphics
|
|
|
namespace opengl
|
|
|
{
|
|
|
|
|
|
-static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers)
|
|
|
+static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, bool clear)
|
|
|
{
|
|
|
// get currently bound fbo to reset to it later
|
|
|
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
|
@@ -100,7 +103,7 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
-static bool createRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
|
|
|
+static bool newRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
|
|
|
{
|
|
|
int reqsamples = samples;
|
|
|
bool unusedSRGB = false;
|
|
@@ -140,8 +143,6 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
|
|
|
|
|
|
if (samples > 1)
|
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
|
|
- else
|
|
|
- samples = 0;
|
|
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
|
@@ -173,7 +174,7 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
|
|
|
{
|
|
|
glDeleteRenderbuffers(1, &buffer);
|
|
|
buffer = 0;
|
|
|
- samples = 0;
|
|
|
+ samples = 1;
|
|
|
}
|
|
|
|
|
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
|
@@ -182,110 +183,221 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
|
|
|
return status == GL_FRAMEBUFFER_COMPLETE;
|
|
|
}
|
|
|
|
|
|
-Canvas::Canvas(const Settings &settings)
|
|
|
- : love::graphics::Texture(settings, nullptr)
|
|
|
+Texture::Texture(const Settings &settings, const Slices *data)
|
|
|
+ : love::graphics::Texture(settings, data)
|
|
|
+ , slices(settings.type)
|
|
|
, fbo(0)
|
|
|
, texture(0)
|
|
|
- , renderbuffer(0)
|
|
|
- , actualSamples(0)
|
|
|
+ , renderbuffer(0)
|
|
|
+ , framebufferStatus(GL_FRAMEBUFFER_COMPLETE)
|
|
|
+ , actualSamples(1)
|
|
|
{
|
|
|
- auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
|
|
- if (gfx != nullptr)
|
|
|
- format = gfx->getSizedFormat(format, renderTarget, readable, sRGB);
|
|
|
-
|
|
|
+ if (data != nullptr)
|
|
|
+ slices = *data;
|
|
|
loadVolatile();
|
|
|
-
|
|
|
- if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(status));
|
|
|
}
|
|
|
|
|
|
-Canvas::~Canvas()
|
|
|
+Texture::~Texture()
|
|
|
{
|
|
|
unloadVolatile();
|
|
|
}
|
|
|
|
|
|
-bool Canvas::loadVolatile()
|
|
|
+bool Texture::createTexture()
|
|
|
{
|
|
|
- if (texture != 0)
|
|
|
- return true;
|
|
|
+ // The base class handles some validation. For example, if ImageData is
|
|
|
+ // given then it must exist for all mip levels, a render target can't use
|
|
|
+ // a compressed format, etc.
|
|
|
|
|
|
- OpenGL::TempDebugGroup debuggroup("Canvas load");
|
|
|
+ glGenTextures(1, &texture);
|
|
|
+ gl.bindTextureToUnit(this, 0, false);
|
|
|
|
|
|
- fbo = texture = 0;
|
|
|
- renderbuffer = 0;
|
|
|
- status = GL_FRAMEBUFFER_COMPLETE;
|
|
|
+ // Use a default texture if the size is too big for the system.
|
|
|
+ // validateDimensions is also called in the base class for RTs and
|
|
|
+ // non-readable textures.
|
|
|
+ if (!renderTarget && !validateDimensions(false))
|
|
|
+ {
|
|
|
+ usingDefaultTexture = true;
|
|
|
|
|
|
- // getMaxRenderbufferSamples will be 0 on systems that don't support
|
|
|
- // multisampled renderbuffers / don't export FBO multisample extensions.
|
|
|
- actualSamples = std::min(getRequestedMSAA(), gl.getMaxRenderbufferSamples());
|
|
|
- actualSamples = std::max(actualSamples, 0);
|
|
|
- actualSamples = actualSamples == 1 ? 0 : actualSamples;
|
|
|
+ setSamplerState(samplerState);
|
|
|
|
|
|
- if (isReadable())
|
|
|
- {
|
|
|
- glGenTextures(1, &texture);
|
|
|
- gl.bindTextureToUnit(this, 0, false);
|
|
|
+ bool isSRGB = false;
|
|
|
+ gl.rawTexStorage(texType, 1, PIXELFORMAT_RGBA8_UNORM, isSRGB, 2, 2, 1);
|
|
|
|
|
|
- GLenum gltype = OpenGL::getGLTextureType(texType);
|
|
|
+ // A nice friendly checkerboard to signify invalid textures...
|
|
|
+ GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF,
|
|
|
+ 0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF};
|
|
|
|
|
|
- if (GLAD_ANGLE_texture_usage)
|
|
|
- glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
|
|
|
+ int slices = texType == TEXTURE_CUBE ? 6 : 1;
|
|
|
+ Rect rect = {0, 0, 2, 2};
|
|
|
+ for (int slice = 0; slice < slices; slice++)
|
|
|
+ uploadByteData(PIXELFORMAT_RGBA8_UNORM, px, sizeof(px), 0, slice, rect, nullptr);
|
|
|
|
|
|
- setSamplerState(samplerState);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- while (glGetError() != GL_NO_ERROR)
|
|
|
- /* Clear the error buffer. */;
|
|
|
+ GLenum gltype = OpenGL::getGLTextureType(texType);
|
|
|
+ if (renderTarget && GLAD_ANGLE_texture_usage)
|
|
|
+ glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
|
|
|
|
|
|
- bool isSRGB = format == PIXELFORMAT_sRGBA8_UNORM;
|
|
|
- if (!gl.rawTexStorage(texType, mipmapCount, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
|
|
|
- {
|
|
|
- status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
|
|
- return false;
|
|
|
- }
|
|
|
+ setSamplerState(samplerState);
|
|
|
|
|
|
- if (glGetError() != GL_NO_ERROR)
|
|
|
- {
|
|
|
- gl.deleteTexture(texture);
|
|
|
- texture = 0;
|
|
|
- status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
|
|
- return false;
|
|
|
- }
|
|
|
+ int mipcount = getMipmapCount();
|
|
|
+ int slicecount = 1;
|
|
|
+
|
|
|
+ if (texType == TEXTURE_VOLUME)
|
|
|
+ slicecount = getDepth();
|
|
|
+ else if (texType == TEXTURE_2D_ARRAY)
|
|
|
+ slicecount = getLayerCount();
|
|
|
+ else if (texType == TEXTURE_CUBE)
|
|
|
+ slicecount = 6;
|
|
|
+
|
|
|
+ if (!isCompressed())
|
|
|
+ gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
|
|
|
+
|
|
|
+ int w = pixelWidth;
|
|
|
+ int h = pixelHeight;
|
|
|
+ int d = depth;
|
|
|
|
|
|
- // Create a local FBO used for glReadPixels as well as MSAA blitting.
|
|
|
- status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers);
|
|
|
+ OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, sRGB);
|
|
|
|
|
|
- if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
|
+ for (int mip = 0; mip < mipcount; mip++)
|
|
|
+ {
|
|
|
+ if (isCompressed() && (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME))
|
|
|
{
|
|
|
- if (fbo != 0)
|
|
|
+ size_t mipsize = 0;
|
|
|
+
|
|
|
+ if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
|
|
+ {
|
|
|
+ for (int slice = 0; slice < slices.getSliceCount(mip); slice++)
|
|
|
+ {
|
|
|
+ auto id = slices.get(slice, mip);
|
|
|
+ if (id != nullptr)
|
|
|
+ mipsize += id->getSize();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mipsize > 0)
|
|
|
{
|
|
|
- gl.deleteFramebuffer(fbo);
|
|
|
- fbo = 0;
|
|
|
+ GLenum gltarget = OpenGL::getGLTextureType(texType);
|
|
|
+ glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr);
|
|
|
}
|
|
|
- return false;
|
|
|
}
|
|
|
+
|
|
|
+ for (int slice = 0; slice < slicecount; slice++)
|
|
|
+ {
|
|
|
+ love::image::ImageDataBase *id = slices.get(slice, mip);
|
|
|
+ if (id != nullptr)
|
|
|
+ uploadImageData(id, mip, slice, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ w = std::max(w / 2, 1);
|
|
|
+ h = std::max(h / 2, 1);
|
|
|
+
|
|
|
+ if (texType == TEXTURE_VOLUME)
|
|
|
+ d = std::max(d / 2, 1);
|
|
|
}
|
|
|
|
|
|
- if (!isReadable() || actualSamples > 0)
|
|
|
- createRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
|
|
|
+ bool hasdata = slices.get(0, 0) != nullptr;
|
|
|
|
|
|
- int64 memsize = getPixelFormatSize(format) * pixelWidth * pixelHeight;
|
|
|
- if (getMipmapCount() > 1)
|
|
|
- memsize *= 1.33334;
|
|
|
+ // Create a local FBO used for glReadPixels as well as MSAA blitting.
|
|
|
+ if (isRenderTarget())
|
|
|
+ {
|
|
|
+ bool clear = !hasdata;
|
|
|
+ int slices = texType == TEXTURE_VOLUME ? depth : layers;
|
|
|
+ framebufferStatus = createFBO(fbo, texType, format, texture, slices, clear);
|
|
|
+ }
|
|
|
+ else if (!hasdata)
|
|
|
+ {
|
|
|
+ // Initialize all slices to transparent black.
|
|
|
+ std::vector<uint8> emptydata(getPixelFormatSliceSize(format, w, h));
|
|
|
+
|
|
|
+ Rect r = {0, 0, w, h};
|
|
|
+ int slices = texType == TEXTURE_VOLUME ? depth : layers;
|
|
|
+ slices = texType == TEXTURE_CUBE ? 6 : slices;
|
|
|
+ for (int i = 0; i < slices; i++)
|
|
|
+ uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Non-readable textures can't have mipmaps (enforced in the base class),
|
|
|
+ // so generateMipmaps here is fine - when they aren't already initialized.
|
|
|
+ if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1)
|
|
|
+ generateMipmaps();
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool Texture::createRenderbuffer()
|
|
|
+{
|
|
|
+ if (isReadable() && actualSamples <= 1)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return newRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
|
|
|
+}
|
|
|
+
|
|
|
+bool Texture::loadVolatile()
|
|
|
+{
|
|
|
+ if (texture != 0 || renderbuffer != 0)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ OpenGL::TempDebugGroup debuggroup("Texture load");
|
|
|
+
|
|
|
+ // NPOT textures don't support mipmapping without full NPOT support.
|
|
|
+ if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
|
|
+ && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
|
|
|
+ {
|
|
|
+ mipmapCount = 1;
|
|
|
+ samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // getMaxRenderbufferSamples will be 0 on systems that don't support
|
|
|
+ // multisampled renderbuffers / don't export FBO multisample extensions.
|
|
|
+ actualSamples = std::min(getRequestedMSAA(), gl.getMaxRenderbufferSamples());
|
|
|
+ actualSamples = std::max(actualSamples, 1);
|
|
|
+
|
|
|
+ while (glGetError() != GL_NO_ERROR); // Clear errors.
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (isReadable())
|
|
|
+ createTexture();
|
|
|
+ if (!isReadable() && actualSamples > 1)
|
|
|
+ createRenderbuffer();
|
|
|
+
|
|
|
+ GLenum glerr = glGetError();
|
|
|
+ if (glerr != GL_NO_ERROR)
|
|
|
+ throw love::Exception("Cannot create texture (OpenGL error: %s)", OpenGL::errorString(glerr));
|
|
|
+ }
|
|
|
+ catch (love::Exception &)
|
|
|
+ {
|
|
|
+ unloadVolatile();
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+
|
|
|
+ int64 memsize = 0;
|
|
|
+
|
|
|
+ for (int mip = 0; mip < getMipmapCount(); mip++)
|
|
|
+ {
|
|
|
+ int w = getPixelWidth(mip);
|
|
|
+ int h = getPixelHeight(mip);
|
|
|
+ int slices = getDepth(mip) * layers * (texType == TEXTURE_CUBE ? 6 : 1);
|
|
|
+ memsize += getPixelFormatSliceSize(format, w, h) * slices;
|
|
|
+ }
|
|
|
|
|
|
if (actualSamples > 1 && isReadable())
|
|
|
- memsize += getPixelFormatSize(format) * pixelWidth * pixelHeight * actualSamples;
|
|
|
+ {
|
|
|
+ int slices = depth * layers * (texType == TEXTURE_CUBE ? 6 : 1);
|
|
|
+ memsize += getPixelFormatSliceSize(format, pixelWidth, pixelHeight) * slices * actualSamples;
|
|
|
+ }
|
|
|
else if (actualSamples > 1)
|
|
|
memsize *= actualSamples;
|
|
|
|
|
|
setGraphicsMemorySize(memsize);
|
|
|
|
|
|
- if (getMipmapCount() > 1)
|
|
|
- generateMipmaps();
|
|
|
-
|
|
|
+ usingDefaultTexture = false;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-void Canvas::unloadVolatile()
|
|
|
+void Texture::unloadVolatile()
|
|
|
{
|
|
|
if (isRenderTarget() && (fbo != 0 || renderbuffer != 0 || texture != 0))
|
|
|
{
|
|
@@ -312,41 +424,73 @@ void Canvas::unloadVolatile()
|
|
|
setGraphicsMemorySize(0);
|
|
|
}
|
|
|
|
|
|
-void Canvas::setSamplerState(const SamplerState &s)
|
|
|
+void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd)
|
|
|
{
|
|
|
- Texture::setSamplerState(s);
|
|
|
+ love::image::ImageDataBase *oldd = slices.get(slice, level);
|
|
|
|
|
|
- if (samplerState.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported())
|
|
|
- throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
|
|
|
+ // We can only replace the internal Data (used when reloading due to setMode)
|
|
|
+ // if the dimensions match.
|
|
|
+ if (imgd != nullptr && oldd != nullptr && oldd->getWidth() == imgd->getWidth()
|
|
|
+ && oldd->getHeight() == imgd->getHeight())
|
|
|
+ {
|
|
|
+ slices.set(slice, level, imgd);
|
|
|
+ }
|
|
|
|
|
|
- if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
|
|
|
+ OpenGL::TempDebugGroup debuggroup("Texture data upload");
|
|
|
+
|
|
|
+ gl.bindTextureToUnit(this, 0, false);
|
|
|
+
|
|
|
+ OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB);
|
|
|
+ GLenum gltarget = OpenGL::getGLTextureType(texType);
|
|
|
+
|
|
|
+ if (texType == TEXTURE_CUBE)
|
|
|
+ gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
|
|
|
+
|
|
|
+ if (isPixelFormatCompressed(pixelformat))
|
|
|
{
|
|
|
- samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST;
|
|
|
+ if (r.x != 0 || r.y != 0)
|
|
|
+ throw love::Exception("x and y parameters must be 0 for compressed textures.");
|
|
|
|
|
|
- if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
|
|
|
- samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST;
|
|
|
+ if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
|
|
+ glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data);
|
|
|
+ else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
|
|
+ glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data);
|
|
|
}
|
|
|
-
|
|
|
- // If we only have limited NPOT support then the wrap mode must be CLAMP.
|
|
|
- if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
|
|
- && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
|
|
|
+ else
|
|
|
{
|
|
|
- samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP;
|
|
|
+ if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
|
|
+ glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data);
|
|
|
+ else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
|
|
+ glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data);
|
|
|
}
|
|
|
-
|
|
|
- gl.bindTextureToUnit(this, 0, false);
|
|
|
- gl.setSamplerState(texType, samplerState);
|
|
|
}
|
|
|
|
|
|
-ptrdiff_t Canvas::getHandle() const
|
|
|
+void Texture::generateMipmaps()
|
|
|
{
|
|
|
- return texture;
|
|
|
+ if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
|
|
|
+ throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
|
|
|
+
|
|
|
+ if (isPixelFormatCompressed(format))
|
|
|
+ throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
|
|
|
+
|
|
|
+ gl.bindTextureToUnit(this, 0, false);
|
|
|
+
|
|
|
+ GLenum gltextype = OpenGL::getGLTextureType(texType);
|
|
|
+
|
|
|
+ if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
|
|
|
+ glEnable(gltextype);
|
|
|
+
|
|
|
+ glGenerateMipmap(gltextype);
|
|
|
}
|
|
|
|
|
|
-love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
|
|
|
+love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
|
|
|
{
|
|
|
+ // Base class does validation (only RTs allowed, etc) and creates ImageData.
|
|
|
love::image::ImageData *data = love::graphics::Texture::newImageData(module, slice, mipmap, r);
|
|
|
|
|
|
+ if (fbo == 0) // Should never be reached.
|
|
|
+ return data;
|
|
|
+
|
|
|
bool isSRGB = false;
|
|
|
OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB);
|
|
|
|
|
@@ -370,53 +514,48 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
-void Canvas::generateMipmaps()
|
|
|
+void Texture::setSamplerState(const SamplerState &s)
|
|
|
{
|
|
|
- if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
|
|
|
- throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
|
|
|
-
|
|
|
- if (isPixelFormatCompressed(format))
|
|
|
- throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
|
|
|
+ if (samplerState.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported())
|
|
|
+ throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
|
|
|
|
|
|
- gl.bindTextureToUnit(this, 0, false);
|
|
|
+ // Base class does common validation and assigns samplerState.
|
|
|
+ love::graphics::Texture::setSamplerState(s);
|
|
|
|
|
|
- GLenum gltextype = OpenGL::getGLTextureType(texType);
|
|
|
+ if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
|
|
|
+ {
|
|
|
+ samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST;
|
|
|
|
|
|
- if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
|
|
|
- glEnable(gltextype);
|
|
|
+ if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
|
|
|
+ samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST;
|
|
|
+ }
|
|
|
|
|
|
- glGenerateMipmap(gltextype);
|
|
|
-}
|
|
|
+ // We don't want filtering or (attempted) mipmaps on the default texture.
|
|
|
+ if (usingDefaultTexture)
|
|
|
+ {
|
|
|
+ samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
|
|
|
+ samplerState.minFilter = samplerState.magFilter = SamplerState::FILTER_NEAREST;
|
|
|
+ }
|
|
|
|
|
|
-void Canvas::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase */*imgd*/)
|
|
|
-{
|
|
|
- OpenGL::TempDebugGroup debuggroup("Texture data upload");
|
|
|
+ // If we only have limited NPOT support then the wrap mode must be CLAMP.
|
|
|
+ if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
|
|
|
+ && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
|
|
|
+ {
|
|
|
+ samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP;
|
|
|
+ }
|
|
|
|
|
|
gl.bindTextureToUnit(this, 0, false);
|
|
|
+ gl.setSamplerState(texType, samplerState);
|
|
|
+}
|
|
|
|
|
|
- OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB);
|
|
|
- GLenum gltarget = OpenGL::getGLTextureType(texType);
|
|
|
-
|
|
|
- if (texType == TEXTURE_CUBE)
|
|
|
- gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
|
|
|
-
|
|
|
- if (isPixelFormatCompressed(pixelformat))
|
|
|
- {
|
|
|
- if (r.x != 0 || r.y != 0)
|
|
|
- throw love::Exception("x and y parameters must be 0 for compressed images.");
|
|
|
+ptrdiff_t Texture::getHandle() const
|
|
|
+{
|
|
|
+ return texture;
|
|
|
+}
|
|
|
|
|
|
- if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
|
|
- glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data);
|
|
|
- else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
|
|
- glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
|
|
- glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data);
|
|
|
- else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
|
|
|
- glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data);
|
|
|
- }
|
|
|
+ptrdiff_t Texture::getRenderTargetHandle() const
|
|
|
+{
|
|
|
+ return renderTarget ? (renderbuffer != 0 ? renderbuffer : texture) : 0;
|
|
|
}
|
|
|
|
|
|
} // opengl
|