mirror of
https://github.com/love2d/love.git
synced 2026-08-12 16:40:57 +02:00
Add sync and async texture and buffer readback APIs.
- Add imagedata = love.graphics.readbackTexture(texture, [slice, mipmap, x, y, w, h, [dest, destx, desty]]). - Add readback = love.graphics.readbackTextureAsync(texture, [slice, mipmap, x, y, w, h, [dest, destx, desty]]). - Add bytedata = love.graphics.readbackBuffer(buffer, [offset, size, [dest, destoffset]]). - Add readback = ove.graphics.readbackBufferAsync(buffer, [offset, size, [dest, destoffset]]). - Deprecate Texture:newImageData. The async variants return a new GraphicsReadback object. It has the following methods: - isComplete() - hasError() - wait() - getBufferData() - getImageData() - update() (called automatically every frame by love, not normally needed). Support notes: - readbackBuffer and readbackBufferAsync require buffer copying support. - readbackTexture with a render target (canvas) is always supported. readbackTexture with a non-render-target requires copy-texture-to-buffer support. - readbackTextureAsync with a render target requires copy-render-target-to-buffer support. readbackTextureAsync with a non-render-target requires copy-texture-to-buffer support.
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2022 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "GraphicsReadback.h"
|
||||
#include "Buffer.h"
|
||||
#include "Texture.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "data/ByteData.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)
|
||||
: love::graphics::GraphicsReadback(gfx, method, buffer, offset, size, dest, destoffset)
|
||||
{
|
||||
// Immediate readback of readback-type buffers doesn't need a staging buffer.
|
||||
if (method != READBACK_IMMEDIATE || buffer->getDataUsage() != BUFFERDATAUSAGE_READBACK)
|
||||
{
|
||||
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
|
||||
gfx->copyBuffer(buffer, stagingBuffer, offset, 0, size);
|
||||
}
|
||||
|
||||
if (method == READBACK_IMMEDIATE)
|
||||
{
|
||||
if (stagingBuffer.get())
|
||||
{
|
||||
status = readbackBuffer(stagingBuffer, 0, size);
|
||||
gfx->releaseTemporaryBuffer(stagingBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = readbackBuffer(buffer, offset, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sync.fence();
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty)
|
||||
: love::graphics::GraphicsReadback(gfx, method, texture, slice, mipmap, rect, dest, destx, desty)
|
||||
{
|
||||
size_t size = getPixelFormatSliceSize(textureFormat, rect.w, rect.h);
|
||||
|
||||
if (method == READBACK_IMMEDIATE)
|
||||
{
|
||||
void *dest = prepareReadbackDest(size);
|
||||
|
||||
love::thread::Lock lock(imageData->getMutex());
|
||||
|
||||
// Direct readback without copying avoids the need for a staging buffer,
|
||||
// and lowers the system requirements of immediate RT readback.
|
||||
Texture *t = (Texture *) texture;
|
||||
t->readbackInternal(slice, mipmap, rect, imageData->getWidth(), size, dest);
|
||||
|
||||
status = STATUS_COMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
|
||||
|
||||
gfx->copyTextureToBuffer(texture, stagingBuffer, slice, mipmap, rect, 0, 0);
|
||||
sync.fence();
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsReadback::~GraphicsReadback()
|
||||
{
|
||||
}
|
||||
|
||||
void GraphicsReadback::wait()
|
||||
{
|
||||
if (status != STATUS_WAITING)
|
||||
return;
|
||||
|
||||
sync.cpuWait();
|
||||
update();
|
||||
}
|
||||
|
||||
void GraphicsReadback::update()
|
||||
{
|
||||
if (status != STATUS_WAITING)
|
||||
return;
|
||||
|
||||
if (sync.isComplete())
|
||||
{
|
||||
if (stagingBuffer.get())
|
||||
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
|
||||
else
|
||||
status = STATUS_ERROR;
|
||||
|
||||
if (stagingBuffer.get())
|
||||
{
|
||||
auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
|
||||
if (gfx != nullptr)
|
||||
gfx->releaseTemporaryBuffer(stagingBuffer);
|
||||
stagingBuffer.set(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
Reference in New Issue
Block a user