Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2022-01-01 15:36:57 -04:00
26 changed files with 543 additions and 248 deletions
+4 -2
View File
@@ -42,7 +42,8 @@ ByteData::ByteData(const void *d, size_t size)
: size(size)
{
create();
memcpy(data, d, size);
if (d != nullptr)
memcpy(data, d, size);
}
ByteData::ByteData(void *d, size_t size, bool own)
@@ -53,7 +54,8 @@ ByteData::ByteData(void *d, size_t size, bool own)
else
{
create();
memcpy(data, d, size);
if (d != nullptr)
memcpy(data, d, size);
}
}
+9 -10
View File
@@ -46,14 +46,10 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
const auto &caps = gfx->getCapabilities();
bool supportsGLSL3 = caps.features[Graphics::FEATURE_GLSL3];
bool indexbuffer = settings.usageFlags & BUFFERUSAGEFLAG_INDEX;
bool vertexbuffer = settings.usageFlags & BUFFERUSAGEFLAG_VERTEX;
bool texelbuffer = settings.usageFlags & BUFFERUSAGEFLAG_TEXEL;
bool storagebuffer = settings.usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE;
bool copydest = settings.usageFlags & BUFFERUSAGEFLAG_COPY_DEST;
if (!indexbuffer && !vertexbuffer && !texelbuffer && !storagebuffer)
throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, texel, or shaderstorage).");
bool indexbuffer = usageFlags & BUFFERUSAGEFLAG_INDEX;
bool vertexbuffer = usageFlags & BUFFERUSAGEFLAG_VERTEX;
bool texelbuffer = usageFlags & BUFFERUSAGEFLAG_TEXEL;
bool storagebuffer = usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE;
if (texelbuffer && !caps.features[Graphics::FEATURE_TEXEL_BUFFER])
throw love::Exception("Texel buffers are not supported on this system.");
@@ -61,8 +57,11 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
if (storagebuffer && !caps.features[Graphics::FEATURE_GLSL4])
throw love::Exception("Shader Storage buffers are not supported on this system (GLSL 4 support is necessary.)");
if (copydest && dataUsage == BUFFERDATAUSAGE_STREAM)
throw love::Exception("Buffers created with 'stream' data usage cannot be used as a copy destination.");
if (storagebuffer && dataUsage == BUFFERDATAUSAGE_STREAM)
throw love::Exception("Buffers created with 'stream' data usage cannot be used as a shader storage buffer.");
if (dataUsage == BUFFERDATAUSAGE_STAGING && (indexbuffer || vertexbuffer || texelbuffer || storagebuffer))
throw love::Exception("Buffers created with 'staging' data usage cannot be index, vertex, texel, or shaderstorage buffer types.");
size_t offset = 0;
size_t stride = 0;
+169 -6
View File
@@ -1104,15 +1104,12 @@ void Graphics::copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, siz
if (!capabilities.features[FEATURE_COPY_BUFFER])
throw love::Exception("Buffer copying is not supported on this system.");
if (!(source->getUsageFlags() & BUFFERUSAGEFLAG_COPY_SOURCE))
throw love::Exception("Copy source buffer must be created with the copysource flag.");
if (!(dest->getUsageFlags() & BUFFERUSAGEFLAG_COPY_DEST))
throw love::Exception("Copy destination buffer must be created with the copydest flag.");
Range sourcerange(sourceoffset, size);
Range destrange(destoffset, size);
if (dest->getDataUsage() == BUFFERDATAUSAGE_STREAM)
throw love::Exception("Buffers created with 'stream' data usage cannot be used as a copy destination.");
if (sourcerange.getMax() >= source->getSize())
throw love::Exception("Buffer copy source offset and size doesn't fit within the source Buffer's size.");
@@ -1125,6 +1122,169 @@ void Graphics::copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, siz
source->copyTo(dest, sourceoffset, destoffset, size);
}
void Graphics::copyTextureToBuffer(Texture *source, Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth)
{
if (!capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER])
{
if (!source->isRenderTarget())
throw love::Exception("Copying a non-render target Texture to a Buffer is not supported on this system.");
if (!capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER])
throw love::Exception("Copying a render target Texture to a Buffer is not supported on this system.");
}
PixelFormat format = source->getPixelFormat();
if (isPixelFormatDepthStencil(format))
throw love::Exception("Copying a depth/stencil Texture to a Buffer is not supported.");
if (!source->isReadable())
throw love::Exception("copyTextureToBuffer can only be called on readable Textures.");
if (dest->getDataUsage() == BUFFERDATAUSAGE_STREAM)
throw love::Exception("Buffers created with 'stream' data usage cannot be used as a copy destination.");
if (isRenderTargetActive(source))
throw love::Exception("copyTextureToBuffer cannot be called while the Texture is an active render target.");
if (mipmap < 0 || mipmap >= source->getMipmapCount())
throw love::Exception("Invalid texture mipmap index %d.", mipmap + 1);
TextureType textype = source->getTextureType();
if (slice < 0 || (textype == TEXTURE_CUBE && slice >= 6)
|| (textype == TEXTURE_VOLUME && slice >= source->getDepth(mipmap))
|| (textype == TEXTURE_2D_ARRAY && slice >= source->getLayerCount()))
{
throw love::Exception("Invalid texture slice index %d.", slice + 1);
}
int mipw = source->getPixelWidth(mipmap);
int miph = source->getPixelHeight(mipmap);
if (rect.x < 0 || rect.y < 0 || rect.w <= 0 || rect.h <= 0
|| (rect.x + rect.w) > mipw || (rect.y + rect.h) > miph)
{
throw love::Exception("Invalid rectangle dimensions (x=%d, y=%d, w=%d, h=%d) for %dx%d texture.", rect.x, rect.y, rect.w, rect.h, mipw, miph);
}
if (destwidth <= 0)
destwidth = rect.w;
size_t size = 0;
if (isPixelFormatCompressed(format))
{
if (destwidth != rect.w) // OpenGL limitation...
throw love::Exception("Copying a compressed texture to a buffer cannot use a custom destination width.");
const PixelFormatInfo &info = getPixelFormatInfo(format);
int bw = (int) info.blockWidth;
int bh = (int) info.blockHeight;
if (rect.x % bw != 0 || rect.y % bh != 0 ||
((rect.w % bw != 0 || rect.h % bh != 0) && rect.x + rect.w != source->getPixelWidth(mipmap)))
{
const char *name = nullptr;
love::getConstant(format, name);
throw love::Exception("Compressed texture format %s only supports copying a sub-rectangle with offset and dimensions that are a multiple of %d x %d.", name, bw, bh);
}
// Note: this will need to change if destwidth == rect.w restriction
// is removed.
size = getPixelFormatSliceSize(format, destwidth, rect.h);
}
else
{
// Not the cleanest, but should work since uncompressed formats always
// have 1x1 blocks.
int pixels = (rect.h - 1) * destwidth + rect.w;
size = getPixelFormatUncompressedRowSize(format, pixels);
}
Range destrange(destoffset, size);
if (destrange.getMax() >= dest->getSize())
throw love::Exception("Buffer copy destination offset and width/height doesn't fit within the destination Buffer.");
source->copyToBuffer(dest, slice, mipmap, rect, destoffset, destwidth, size);
}
void Graphics::copyBufferToTexture(Buffer *source, Texture *dest, size_t sourceoffset, int sourcewidth, int slice, int mipmap, const Rect &rect)
{
if (!capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE])
throw love::Exception("Copying a Buffer to a Texture is not supported on this system.");
PixelFormat format = dest->getPixelFormat();
if (isPixelFormatDepthStencil(format))
throw love::Exception("Copying a Buffer to a depth/stencil Texture is not supported.");
if (!dest->isReadable())
throw love::Exception("copyBufferToTexture can only be called on readable Textures.");
if (isRenderTargetActive(dest))
throw love::Exception("copyBufferToTexture cannot be called while the Texture is an active render target.");
if (mipmap < 0 || mipmap >= dest->getMipmapCount())
throw love::Exception("Invalid texture mipmap index %d.", mipmap + 1);
TextureType textype = dest->getTextureType();
if (slice < 0 || (textype == TEXTURE_CUBE && slice >= 6)
|| (textype == TEXTURE_VOLUME && slice >= dest->getDepth(mipmap))
|| (textype == TEXTURE_2D_ARRAY && slice >= dest->getLayerCount()))
{
throw love::Exception("Invalid texture slice index %d.", slice + 1);
}
int mipw = dest->getPixelWidth(mipmap);
int miph = dest->getPixelHeight(mipmap);
if (rect.x < 0 || rect.y < 0 || rect.w <= 0 || rect.h <= 0
|| (rect.x + rect.w) > mipw || (rect.y + rect.h) > miph)
{
throw love::Exception("Invalid rectangle dimensions (x=%d, y=%d, w=%d, h=%d) for %dx%d texture.", rect.x, rect.y, rect.w, rect.h, mipw, miph);
}
if (sourcewidth <= 0)
sourcewidth = rect.w;
size_t size = 0;
if (isPixelFormatCompressed(format))
{
if (sourcewidth != rect.w) // OpenGL limitation...
throw love::Exception("Copying a buffer to a compressed texture cannot use a custom source width.");
const PixelFormatInfo &info = getPixelFormatInfo(format);
int bw = (int) info.blockWidth;
int bh = (int) info.blockHeight;
if (rect.x % bw != 0 || rect.y % bh != 0 ||
((rect.w % bw != 0 || rect.h % bh != 0) && rect.x + rect.w != dest->getPixelWidth(mipmap)))
{
const char *name = nullptr;
love::getConstant(format, name);
throw love::Exception("Compressed texture format %s only supports copying a sub-rectangle with offset and dimensions that are a multiple of %d x %d.", name, bw, bh);
}
// Note: this will need to change if sourcewidth == rect.w restriction
// is removed.
size = getPixelFormatSliceSize(format, sourcewidth, rect.h);
}
else
{
// Not the cleanest, but should work since uncompressed formats always
// have 1x1 blocks.
int pixels = (rect.h - 1) * sourcewidth + rect.w;
size = getPixelFormatUncompressedRowSize(format, pixels);
}
Range sourcerange(sourceoffset, size);
if (sourcerange.getMax() >= source->getSize())
throw love::Exception("Buffer copy source offset and width/height doesn't fit within the source Buffer.");
dest->copyFromBuffer(source, sourceoffset, sourcewidth, size, slice, mipmap, rect);
}
void Graphics::dispatchThreadgroups(Shader* shader, int x, int y, int z)
{
if (!shader->hasStage(SHADERSTAGE_COMPUTE))
@@ -1956,6 +2116,9 @@ STRINGMAP_CLASS_BEGIN(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, f
{ "instancing", Graphics::FEATURE_INSTANCING },
{ "texelbuffer", Graphics::FEATURE_TEXEL_BUFFER },
{ "copybuffer", Graphics::FEATURE_COPY_BUFFER },
{ "copybuffertotexture", Graphics::FEATURE_COPY_BUFFER_TO_TEXTURE },
{ "copytexturetobuffer", Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER },
{ "copyrendertargettobuffer", Graphics::FEATURE_COPY_RENDER_TARGET_TO_BUFFER },
}
STRINGMAP_CLASS_END(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, feature)
+5
View File
@@ -145,6 +145,9 @@ public:
FEATURE_INSTANCING,
FEATURE_TEXEL_BUFFER,
FEATURE_COPY_BUFFER,
FEATURE_COPY_BUFFER_TO_TEXTURE,
FEATURE_COPY_TEXTURE_TO_BUFFER,
FEATURE_COPY_RENDER_TARGET_TO_BUFFER,
FEATURE_MAX_ENUM
};
@@ -675,6 +678,8 @@ public:
void captureScreenshot(const ScreenshotInfo &info);
void copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size);
void copyTextureToBuffer(Texture *source, Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth);
void copyBufferToTexture(Buffer *source, Texture *dest, size_t sourceoffset, int sourcewidth, int slice, int mipmap, const Rect &rect);
void dispatchThreadgroups(Shader* shader, int x, int y, int z);
+11 -2
View File
@@ -473,9 +473,18 @@ void Texture::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap
throw love::Exception("Invalid rectangle dimensions (x=%d, y=%d, w=%d, h=%d) for %dx%d Texture.", rect.x, rect.y, rect.w, rect.h, mipw, miph);
}
// We don't currently support partial updates of compressed textures.
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.");
{
const PixelFormatInfo &info = getPixelFormatInfo(d->getFormat());
int bw = (int) info.blockWidth;
int bh = (int) info.blockHeight;
if (rect.x % bw != 0 || rect.y % bh != 0 || rect.w % bw != 0 || rect.h % bh != 0)
{
const char *name = nullptr;
love::getConstant(d->getFormat(), name);
throw love::Exception("Compressed texture format %s only supports replacing a sub-rectangle with offset and dimensions that are a multiple of %d x %d.", name, bw, bh);
}
}
Graphics::flushBatchedDrawsGlobal();
+4
View File
@@ -46,6 +46,7 @@ namespace graphics
{
class Graphics;
class Buffer;
enum TextureType
{
@@ -246,6 +247,9 @@ public:
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
virtual void copyFromBuffer(Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) = 0;
virtual void copyToBuffer(Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) = 0;
virtual ptrdiff_t getRenderTargetHandle() const = 0;
virtual ptrdiff_t getSamplerHandle() const = 0;
+4 -1
View File
@@ -2100,7 +2100,10 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_INSTANCING] = true;
capabilities.features[FEATURE_TEXEL_BUFFER] = true;
capabilities.features[FEATURE_COPY_BUFFER] = true;
static_assert(FEATURE_MAX_ENUM == 12, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = true;
static_assert(FEATURE_MAX_ENUM == 15, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
capabilities.limits[LIMIT_POINT_SIZE] = 511;
+3
View File
@@ -40,6 +40,9 @@ public:
Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const Slices *data);
virtual ~Texture();
void copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
void copyToBuffer(love::graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override;
void setSamplerState(const SamplerState &s) override;
ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
+10
View File
@@ -258,6 +258,16 @@ void Texture::readbackImageData(love::image::ImageData *imagedata, int slice, in
memcpy(imagedata->getData(), buffer.contents, imagedata->getSize());
}}
void Texture::copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect)
{
// TODO
}
void Texture::copyToBuffer(love::graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size)
{
// TODO
}
void Texture::setSamplerState(const SamplerState &s)
{ @autoreleasepool {
// Base class does common validation and assigns samplerState.
+11 -10
View File
@@ -80,10 +80,6 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
mapUsage = BUFFERUSAGE_INDEX;
else if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
mapUsage = BUFFERUSAGE_SHADER_STORAGE;
else if (usageFlags & BUFFERUSAGEFLAG_COPY_SOURCE)
mapUsage = BUFFERUSAGE_COPY_SOURCE;
else if (usageFlags & BUFFERUSAGEFLAG_COPY_DEST)
mapUsage = BUFFERUSAGE_COPY_DEST;
target = OpenGL::getGLBufferType(mapUsage);
@@ -161,6 +157,11 @@ bool Buffer::load(const void *initialdata)
return (glGetError() == GL_NO_ERROR);
}
bool Buffer::supportsOrphan() const
{
return dataUsage == BUFFERDATAUSAGE_STREAM || dataUsage == BUFFERDATAUSAGE_DYNAMIC;
}
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
{
if (size == 0)
@@ -206,7 +207,7 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize)
mapped = false;
// Orphan optimization - see fill().
if (dataUsage != BUFFERDATAUSAGE_STATIC && mappedRange.first == 0 && mappedRange.getSize() == getSize())
if (supportsOrphan() && mappedRange.first == 0 && mappedRange.getSize() == getSize())
{
usedoffset = 0;
usedsize = getSize();
@@ -238,15 +239,14 @@ void Buffer::fill(size_t offset, size_t size, const void *data)
gl.bindBuffer(mapUsage, buffer);
if (dataUsage != BUFFERDATAUSAGE_STATIC && size == buffersize)
if (supportsOrphan() && size == buffersize)
{
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
gl.bindBuffer(mapUsage, buffer);
glBufferData(target, (GLsizeiptr) buffersize, nullptr, gldatausage);
#if LOVE_WINDOWS
// TODO: Verify that this codepath is a useful optimization.
// TODO: Verify that this intel codepath is a useful optimization.
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
glBufferData(target, (GLsizeiptr) buffersize, data, gldatausage);
else
@@ -261,8 +261,9 @@ void Buffer::fill(size_t offset, size_t size, const void *data)
void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
{
gl.bindBuffer(BUFFERUSAGE_COPY_SOURCE, buffer);
gl.bindBuffer(BUFFERUSAGE_COPY_DEST, ((Buffer *) dest)->buffer);
// TODO: tracked state for these bind types?
glBindBuffer(GL_COPY_READ_BUFFER, buffer);
glBindBuffer(GL_COPY_WRITE_BUFFER, ((Buffer *) dest)->buffer);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, sourceoffset, destoffset, size);
}
+1 -3
View File
@@ -61,9 +61,7 @@ public:
private:
bool load(const void *initialdata);
void unmapStatic(size_t offset, size_t size);
void unmapStream();
bool supportsOrphan() const;
BufferUsage mapUsage = BUFFERUSAGE_VERTEX;
GLenum target = 0;
+7 -5
View File
@@ -509,8 +509,7 @@ static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarri
if (usage & BUFFERUSAGEFLAG_VERTEX)
postDispatchBarriers |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
if (usage & (BUFFERUSAGEFLAG_COPY_SOURCE | BUFFERUSAGEFLAG_COPY_DEST))
postDispatchBarriers |= GL_PIXEL_BUFFER_BARRIER_BIT;
postDispatchBarriers |= GL_PIXEL_BUFFER_BARRIER_BIT;
}
for (const auto &binding : shader->getStorageTextureBindings())
@@ -858,7 +857,7 @@ void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth
if (c.hasValue)
{
Colorf cf((float)c.value.r, (float)c.value.g, (float)c.value.b, (float)c.value.b);
Colorf cf((float)c.value.r, (float)c.value.g, (float)c.value.b, (float)c.value.a);
gammaCorrectColor(cf);
glClearColor(cf.r, cf.g, cf.b, cf.a);
flags |= GL_COLOR_BUFFER_BIT;
@@ -1658,8 +1657,11 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferUsageSupported(BUFFERUSAGE_TEXEL);
capabilities.features[FEATURE_COPY_BUFFER] = gl.isBufferUsageSupported(BUFFERUSAGE_COPY_SOURCE);
static_assert(FEATURE_MAX_ENUM == 12, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_COPY_BUFFER] = gl.isCopyBufferSupported();
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = gl.isCopyBufferToTextureSupported();
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported();
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = gl.isCopyRenderTargetToBufferSupported();
static_assert(FEATURE_MAX_ENUM == 15, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
+25 -5
View File
@@ -666,8 +666,6 @@ GLenum OpenGL::getGLBufferType(BufferUsage usage)
case BUFFERUSAGE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
case BUFFERUSAGE_TEXEL: return GL_TEXTURE_BUFFER;
case BUFFERUSAGE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
case BUFFERUSAGE_COPY_SOURCE: return GL_COPY_READ_BUFFER;
case BUFFERUSAGE_COPY_DEST: return GL_COPY_WRITE_BUFFER;
case BUFFERUSAGE_MAX_ENUM: return GL_ZERO;
}
@@ -844,6 +842,8 @@ GLenum OpenGL::getGLBufferDataUsage(BufferDataUsage usage)
case BUFFERDATAUSAGE_STREAM: return GL_STREAM_DRAW;
case BUFFERDATAUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW;
case BUFFERDATAUSAGE_STATIC: return GL_STATIC_DRAW;
case BUFFERDATAUSAGE_STAGING:
return (GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0) ? GL_STREAM_READ : GL_STREAM_DRAW;
default: return 0;
}
}
@@ -1483,9 +1483,6 @@ bool OpenGL::isBufferUsageSupported(BufferUsage usage) const
return GLAD_VERSION_3_1;
case BUFFERUSAGE_SHADER_STORAGE:
return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
case BUFFERUSAGE_COPY_SOURCE:
case BUFFERUSAGE_COPY_DEST:
return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_0;
case BUFFERUSAGE_MAX_ENUM:
return false;
}
@@ -1530,6 +1527,29 @@ bool OpenGL::isMultiFormatMRTSupported() const
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
bool OpenGL::isCopyBufferSupported() const
{
return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_0;
}
bool OpenGL::isCopyBufferToTextureSupported() const
{
// Requires pixel unpack buffer binding support.
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0;
}
bool OpenGL::isCopyTextureToBufferSupported() const
{
// Requires glGetTextureSubImage support.
return GLAD_VERSION_4_5 || GLAD_ARB_get_texture_sub_image;
}
bool OpenGL::isCopyRenderTargetToBufferSupported() const
{
// Requires pixel pack buffer binding support.
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0;
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
+4
View File
@@ -371,6 +371,10 @@ public:
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
bool isMultiFormatMRTSupported() const;
bool isCopyBufferSupported() const;
bool isCopyBufferToTextureSupported() const;
bool isCopyTextureToBufferSupported() const;
bool isCopyRenderTargetToBufferSupported() const;
/**
* Returns the maximum supported width or height of a texture.
+78 -5
View File
@@ -22,6 +22,7 @@
#include "graphics/Graphics.h"
#include "Graphics.h"
#include "Buffer.h"
#include "common/int.h"
// STD
@@ -466,13 +467,16 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
if (isPixelFormatCompressed(pixelformat))
{
if (r.x != 0 || r.y != 0)
throw love::Exception("x and y parameters must be 0 for compressed textures.");
if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data);
{
// Possible issues on some very old drivers if TexSubImage is used.
if (r.x != 0 || r.y != 0 || r.w != getPixelWidth(level) || r.h != getPixelHeight(level))
glCompressedTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.internalformat, size, data);
else
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);
glCompressedTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.internalformat, size, data);
}
else
{
@@ -521,6 +525,75 @@ void Texture::readbackImageData(love::image::ImageData *data, int slice, int mip
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
}
void Texture::copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect)
{
// Higher level code does validation.
GLuint glbuffer = (GLuint) source->getHandle();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glbuffer);
if (!isCompressed()) // Not supported in GL with compressed textures...
glPixelStorei(GL_UNPACK_ROW_LENGTH, sourcewidth);
// glTexSubImage and friends copy from the active pixel_unpack_buffer by
// treating the pointer as a byte offset.
const uint8 *byteoffset = (const uint8 *)(ptrdiff_t)sourceoffset;
uploadByteData(format, byteoffset, size, mipmap, slice, rect);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
void Texture::copyToBuffer(love::graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size)
{
// Higher level code does validation.
GLuint glbuffer = (GLuint) dest->getHandle();
glBindBuffer(GL_PIXEL_PACK_BUFFER, glbuffer);
if (!isCompressed()) // Not supported in GL with compressed textures...
glPixelStorei(GL_PACK_ROW_LENGTH, destwidth);
gl.bindTextureToUnit(this, 0, false);
bool isSRGB = false;
OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, isSRGB);
// glTexSubImage and friends copy from the active pixel_unpack_buffer by
// treating the pointer as a byte offset.
uint8 *byteoffset = (uint8 *)(ptrdiff_t)destoffset;
if (gl.isCopyTextureToBufferSupported())
{
if (isCompressed())
glGetCompressedTextureSubImage(texture, mipmap, rect.x, rect.y, slice, rect.w, rect.h, 1, size, byteoffset);
else
glGetTextureSubImage(texture, mipmap, rect.x, rect.y, slice, rect.w, rect.h, 1, fmt.externalformat, fmt.type, size, byteoffset);
}
else if (fbo)
{
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
if (slice > 0 || mipmap > 0)
{
int layer = texType == TEXTURE_CUBE ? 0 : slice;
int face = texType == TEXTURE_CUBE ? slice : 0;
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, mipmap, layer, face);
}
glReadPixels(rect.x, rect.y, rect.w, rect.h, fmt.externalformat, fmt.type, byteoffset);
if (slice > 0 || mipmap > 0)
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
}
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
void Texture::setSamplerState(const SamplerState &s)
{
if (s.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported())
+3
View File
@@ -46,6 +46,9 @@ public:
bool loadVolatile() override;
void unloadVolatile() override;
void copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
void copyToBuffer(love::graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override;
void setSamplerState(const SamplerState &s) override;
ptrdiff_t getHandle() const override;
+1 -2
View File
@@ -371,8 +371,6 @@ STRINGMAP_BEGIN(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsageName)
{ "index", BUFFERUSAGE_INDEX },
{ "texel", BUFFERUSAGE_TEXEL },
{ "shaderstorage", BUFFERUSAGE_SHADER_STORAGE },
{ "copysource", BUFFERUSAGE_COPY_SOURCE },
{ "copydest", BUFFERUSAGE_COPY_DEST },
}
STRINGMAP_END(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsageName)
@@ -388,6 +386,7 @@ STRINGMAP_BEGIN(BufferDataUsage, BUFFERDATAUSAGE_MAX_ENUM, bufferDataUsage)
{ "stream", BUFFERDATAUSAGE_STREAM },
{ "dynamic", BUFFERDATAUSAGE_DYNAMIC },
{ "static", BUFFERDATAUSAGE_STATIC },
{ "staging", BUFFERDATAUSAGE_STAGING },
}
STRINGMAP_END(BufferDataUsage, BUFFERDATAUSAGE_MAX_ENUM, bufferDataUsage)
+1 -4
View File
@@ -60,8 +60,6 @@ enum BufferUsage
BUFFERUSAGE_INDEX,
BUFFERUSAGE_TEXEL,
BUFFERUSAGE_SHADER_STORAGE,
BUFFERUSAGE_COPY_SOURCE,
BUFFERUSAGE_COPY_DEST,
BUFFERUSAGE_MAX_ENUM
};
@@ -72,8 +70,6 @@ enum BufferUsageFlags
BUFFERUSAGEFLAG_INDEX = 1 << BUFFERUSAGE_INDEX,
BUFFERUSAGEFLAG_TEXEL = 1 << BUFFERUSAGE_TEXEL,
BUFFERUSAGEFLAG_SHADER_STORAGE = 1 << BUFFERUSAGE_SHADER_STORAGE,
BUFFERUSAGEFLAG_COPY_SOURCE = 1 << BUFFERUSAGE_COPY_SOURCE,
BUFFERUSAGEFLAG_COPY_DEST = 1 << BUFFERUSAGE_COPY_DEST,
};
enum IndexDataType
@@ -114,6 +110,7 @@ enum BufferDataUsage
BUFFERDATAUSAGE_STREAM,
BUFFERDATAUSAGE_DYNAMIC,
BUFFERDATAUSAGE_STATIC,
BUFFERDATAUSAGE_STAGING,
BUFFERDATAUSAGE_MAX_ENUM
};
+66
View File
@@ -3353,6 +3353,70 @@ int w_copyBuffer(lua_State *L)
return 0;
}
int w_copyBufferToTexture(lua_State *L)
{
Buffer *source = luax_checkbuffer(L, 1);
Texture *dest = luax_checktexture(L, 2);
ptrdiff_t sourceoffset = luaL_optinteger(L, 3, 0);
if (sourceoffset < 0)
return luaL_error(L, "copyBufferToTexture source offset cannot be negative.");
int sourcewidth = (int) luaL_optinteger(L, 4, 0);
int slice = 0;
int mipmap = 0;
if (dest->getTextureType() != TEXTURE_2D)
slice = (int) luaL_checkinteger(L, 5) - 1;
mipmap = (int) luaL_optinteger(L, 6, 1) - 1;
Rect rect = {0, 0, dest->getPixelWidth(mipmap), dest->getPixelHeight(mipmap)};
if (!lua_isnoneornil(L, 7))
{
rect.x = (int) luaL_checkinteger(L, 7);
rect.y = (int) luaL_checkinteger(L, 8);
rect.w = (int) luaL_checkinteger(L, 9);
rect.h = (int) luaL_checkinteger(L, 10);
}
luax_catchexcept(L, [&](){ instance()->copyBufferToTexture(source, dest, sourceoffset, sourcewidth, slice, mipmap, rect); });
return 0;
}
int w_copyTextureToBuffer(lua_State *L)
{
Texture *source = luax_checktexture(L, 1);
Buffer *dest = luax_checkbuffer(L, 2);
int slice = 0;
int mipmap = 0;
if (source->getTextureType() != TEXTURE_2D)
slice = (int) luaL_checkinteger(L, 3) - 1;
mipmap = (int) luaL_optinteger(L, 4, 1) - 1;
Rect rect = {0, 0, source->getPixelWidth(mipmap), source->getPixelHeight(mipmap)};
if (!lua_isnoneornil(L, 5))
{
rect.x = (int) luaL_checkinteger(L, 5);
rect.y = (int) luaL_checkinteger(L, 6);
rect.w = (int) luaL_checkinteger(L, 7);
rect.h = (int) luaL_checkinteger(L, 8);
}
ptrdiff_t destoffset = luaL_optinteger(L, 9, 0);
if (destoffset < 0)
return luaL_error(L, "copyTextureToBuffer dest offset cannot be negative.");
int destwidth = (int) luaL_optinteger(L, 10, 0);
luax_catchexcept(L, [&](){ instance()->copyTextureToBuffer(source, dest, slice, mipmap, rect, destoffset, destwidth); });
return 0;
}
int w_flushBatch(lua_State *)
{
instance()->flushBatchedDraws();
@@ -3553,6 +3617,8 @@ static const luaL_Reg functions[] =
{ "dispatchThreadgroups", w_dispatchThreadgroups },
{ "copyBuffer", w_copyBuffer },
{ "copyBufferToTexture", w_copyBufferToTexture },
{ "copyTextureToBuffer", w_copyTextureToBuffer },
{ "isCreated", w_isCreated },
{ "isActive", w_isActive },
+1 -1
View File
@@ -390,13 +390,13 @@ int w_Texture_newImageData(lua_State *L)
int slice = 0;
int mipmap = 0;
Rect rect = {0, 0, t->getPixelWidth(), t->getPixelHeight()};
if (t->getTextureType() != TEXTURE_2D)
slice = (int) luaL_checkinteger(L, 2) - 1;
mipmap = (int) luaL_optinteger(L, 3, 1) - 1;
Rect rect = {0, 0, t->getPixelWidth(mipmap), t->getPixelHeight(mipmap)};
if (!lua_isnoneornil(L, 4))
{
rect.x = (int) luaL_checkinteger(L, 4);