mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 20:20:19 +02:00
Merge pull request #1372 from AverageConsumer/codex/android-async-canvas-readback
This commit is contained in:
@@ -154,6 +154,16 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
|||||||
return module->newImageData(r.w, r.h, dataformat);
|
return module->newImageData(r.w, r.h, dataformat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::requestImageData()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
love::image::ImageData *Canvas::pollImageData(love::image::Image *)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::draw(Graphics *gfx, Quad *q, const Matrix4 &t)
|
void Canvas::draw(Graphics *gfx, Quad *q, const Matrix4 &t)
|
||||||
{
|
{
|
||||||
if (gfx->isCanvasActive(this))
|
if (gfx->isCanvasActive(this))
|
||||||
@@ -232,4 +242,3 @@ StringMap<Canvas::SettingType, Canvas::SETTING_MAX_ENUM> Canvas::settingTypes(Ca
|
|||||||
|
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ public:
|
|||||||
int getRequestedMSAA() const;
|
int getRequestedMSAA() const;
|
||||||
|
|
||||||
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
|
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
|
||||||
|
virtual bool requestImageData();
|
||||||
|
virtual love::image::ImageData *pollImageData(love::image::Image *module);
|
||||||
virtual void generateMipmaps() = 0;
|
virtual void generateMipmaps() = 0;
|
||||||
|
|
||||||
virtual int getMSAA() const = 0;
|
virtual int getMSAA() const = 0;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
|
|
||||||
#include <algorithm> // For min/max
|
#include <algorithm> // For min/max
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -197,6 +198,9 @@ Canvas::Canvas(const Settings &settings)
|
|||||||
, texture(0)
|
, texture(0)
|
||||||
, renderbuffer(0)
|
, renderbuffer(0)
|
||||||
, actualSamples(0)
|
, actualSamples(0)
|
||||||
|
, readbackBuffer(0)
|
||||||
|
, readbackFence(nullptr)
|
||||||
|
, readbackSize(0)
|
||||||
{
|
{
|
||||||
format = getSizedFormat(format);
|
format = getSizedFormat(format);
|
||||||
|
|
||||||
@@ -314,6 +318,14 @@ bool Canvas::loadVolatile()
|
|||||||
|
|
||||||
void Canvas::unloadVolatile()
|
void Canvas::unloadVolatile()
|
||||||
{
|
{
|
||||||
|
if (readbackFence != nullptr)
|
||||||
|
glDeleteSync(readbackFence);
|
||||||
|
if (readbackBuffer != 0)
|
||||||
|
glDeleteBuffers(1, &readbackBuffer);
|
||||||
|
readbackFence = nullptr;
|
||||||
|
readbackBuffer = 0;
|
||||||
|
readbackSize = 0;
|
||||||
|
|
||||||
if (fbo != 0 || renderbuffer != 0 || texture != 0)
|
if (fbo != 0 || renderbuffer != 0 || texture != 0)
|
||||||
{
|
{
|
||||||
// This is a bit ugly, but we need some way to destroy the cached FBO
|
// This is a bit ugly, but we need some way to destroy the cached FBO
|
||||||
@@ -480,6 +492,68 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::requestImageData()
|
||||||
|
{
|
||||||
|
if (readbackFence != nullptr || !isReadable()
|
||||||
|
|| !(GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_2)
|
||||||
|
|| texType != TEXTURE_2D
|
||||||
|
|| (format != PIXELFORMAT_RGBA8 && format != PIXELFORMAT_sRGBA8)
|
||||||
|
|| actualSamples > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||||
|
if (gfx != nullptr && gfx->isCanvasActive(this))
|
||||||
|
throw love::Exception("Canvas:requestImageData cannot be called while that Canvas is active.");
|
||||||
|
|
||||||
|
const size_t size = (size_t) pixelWidth * (size_t) pixelHeight * 4;
|
||||||
|
if (readbackBuffer == 0)
|
||||||
|
glGenBuffers(1, &readbackBuffer);
|
||||||
|
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, readbackBuffer);
|
||||||
|
if (readbackSize != size)
|
||||||
|
{
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER, size, nullptr, GL_STREAM_READ);
|
||||||
|
readbackSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint currentfbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
|
||||||
|
glReadPixels(0, 0, pixelWidth, pixelHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
readbackFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, currentfbo);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
return readbackFence != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
love::image::ImageData *Canvas::pollImageData(love::image::Image *module)
|
||||||
|
{
|
||||||
|
if (readbackFence == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
GLenum status = glClientWaitSync(readbackFence, 0, 0);
|
||||||
|
if (status == GL_TIMEOUT_EXPIRED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
glDeleteSync(readbackFence);
|
||||||
|
readbackFence = nullptr;
|
||||||
|
if (status == GL_WAIT_FAILED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
love::image::ImageData *data = module->newImageData(pixelWidth, pixelHeight, PIXELFORMAT_RGBA8);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, readbackBuffer);
|
||||||
|
void *pixels = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, readbackSize, GL_MAP_READ_BIT);
|
||||||
|
if (pixels == nullptr)
|
||||||
|
{
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
data->release();
|
||||||
|
throw love::Exception("Could not map asynchronous Canvas readback.");
|
||||||
|
}
|
||||||
|
memcpy(data->getData(), pixels, readbackSize);
|
||||||
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::generateMipmaps()
|
void Canvas::generateMipmaps()
|
||||||
{
|
{
|
||||||
if (getMipmapCount() == 1 || getMipmapMode() == MIPMAPS_NONE)
|
if (getMipmapCount() == 1 || getMipmapMode() == MIPMAPS_NONE)
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
ptrdiff_t getHandle() const override;
|
ptrdiff_t getHandle() const override;
|
||||||
|
|
||||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
||||||
|
bool requestImageData() override;
|
||||||
|
love::image::ImageData *pollImageData(love::image::Image *module) override;
|
||||||
void generateMipmaps() override;
|
void generateMipmaps() override;
|
||||||
|
|
||||||
int getMSAA() const override
|
int getMSAA() const override
|
||||||
@@ -107,6 +109,9 @@ private:
|
|||||||
GLenum status;
|
GLenum status;
|
||||||
|
|
||||||
int actualSamples;
|
int actualSamples;
|
||||||
|
GLuint readbackBuffer;
|
||||||
|
GLsync readbackFence;
|
||||||
|
size_t readbackSize;
|
||||||
|
|
||||||
static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
|
static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
|
||||||
static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
|
static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
|
||||||
|
|||||||
@@ -116,6 +116,31 @@ int w_Canvas_newImageData(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Canvas_requestImageData(lua_State *L)
|
||||||
|
{
|
||||||
|
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||||
|
bool requested = false;
|
||||||
|
luax_catchexcept(L, [&](){ requested = canvas->requestImageData(); });
|
||||||
|
luax_pushboolean(L, requested);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_Canvas_pollImageData(lua_State *L)
|
||||||
|
{
|
||||||
|
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||||
|
love::image::Image *image = luax_getmodule<love::image::Image>(L, love::image::Image::type);
|
||||||
|
love::image::ImageData *data = nullptr;
|
||||||
|
luax_catchexcept(L, [&](){ data = canvas->pollImageData(image); });
|
||||||
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
luax_pushtype(L, data);
|
||||||
|
data->release();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_Canvas_generateMipmaps(lua_State *L)
|
int w_Canvas_generateMipmaps(lua_State *L)
|
||||||
{
|
{
|
||||||
Canvas *c = luax_checkcanvas(L, 1);
|
Canvas *c = luax_checkcanvas(L, 1);
|
||||||
@@ -139,6 +164,8 @@ static const luaL_Reg w_Canvas_functions[] =
|
|||||||
{ "getMSAA", w_Canvas_getMSAA },
|
{ "getMSAA", w_Canvas_getMSAA },
|
||||||
{ "renderTo", w_Canvas_renderTo },
|
{ "renderTo", w_Canvas_renderTo },
|
||||||
{ "newImageData", w_Canvas_newImageData },
|
{ "newImageData", w_Canvas_newImageData },
|
||||||
|
{ "requestImageData", w_Canvas_requestImageData },
|
||||||
|
{ "pollImageData", w_Canvas_pollImageData },
|
||||||
{ "generateMipmaps", w_Canvas_generateMipmaps },
|
{ "generateMipmaps", w_Canvas_generateMipmaps },
|
||||||
{ "getMipmapMode", w_Canvas_getMipmapMode },
|
{ "getMipmapMode", w_Canvas_getMipmapMode },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
|
|||||||
Reference in New Issue
Block a user