mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user