This commit is contained in:
halx99 2024-09-20 07:30:12 +08:00 committed by GitHub
parent 1e178b8273
commit 805390e9d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 43 deletions

View File

@ -40,6 +40,9 @@ THE SOFTWARE.
#include "renderer/backend/DriverBase.h"
#include "renderer/backend/Texture.h"
#include "renderer/backend/RenderTarget.h"
#if defined(AX_USE_GL)
# include "renderer/backend/opengl/CommandBufferGL.h"
#endif
namespace ax
{
@ -72,12 +75,14 @@ void RenderTexture::listenToBackground(EventCustom* /*event*/)
// We have not found a way to dispatch the enter background message before the texture data are destroyed.
// So we disable this pair of message handler at present.
#if AX_ENABLE_CACHE_TEXTURE_DATA
if (!_cachedTextureDirty)
return;
// to get the rendered texture data
auto func = [&](RefPtr<Image> uiTextureImage) {
if (uiTextureImage)
{
_UITextureImage = uiTextureImage;
const Vec2& s = _texture2D->getContentSizeInPixels();
const Vec2& s = _texture2D->getContentSizeInPixels();
VolatileTextureMgr::addDataTexture(_texture2D, uiTextureImage->getData(), s.width * s.height * 4,
backend::PixelFormat::RGBA8, s);
}
@ -87,8 +92,7 @@ void RenderTexture::listenToBackground(EventCustom* /*event*/)
}
};
auto callback = std::bind(func, std::placeholders::_1);
newImage(callback, false);
newImage(callback, true);
#endif
}
@ -112,7 +116,11 @@ RenderTexture* RenderTexture::create(int w, int h, backend::PixelFormat eFormat,
return nullptr;
}
RenderTexture* RenderTexture::create(int w, int h, backend::PixelFormat eFormat, PixelFormat uDepthStencilFormat, bool sharedRenderTarget)
RenderTexture* RenderTexture::create(int w,
int h,
backend::PixelFormat eFormat,
PixelFormat uDepthStencilFormat,
bool sharedRenderTarget)
{
RenderTexture* ret = new RenderTexture();
@ -149,7 +157,8 @@ bool RenderTexture::initWithWidthAndHeight(int w,
PixelFormat depthStencilFormat,
bool sharedRenderTarget)
{
AXASSERT(format == backend::PixelFormat::RGBA8 || format == PixelFormat::RGB8 || format == PixelFormat::RGBA4, "only RGB and RGBA formats are valid for a render texture");
AXASSERT(format == backend::PixelFormat::RGBA8 || format == PixelFormat::RGB8 || format == PixelFormat::RGBA4,
"only RGB and RGBA formats are valid for a render texture");
bool ret = false;
do
@ -160,7 +169,7 @@ bool RenderTexture::initWithWidthAndHeight(int w,
_fullviewPort = Rect(0, 0, w, h);
setContentSize(Vec2(static_cast<float>(w), static_cast<float>(h)));
// textures must be power of two squared
int powW = 0;
int powH = 0;
@ -203,10 +212,10 @@ bool RenderTexture::initWithWidthAndHeight(int w,
}
else
{
_renderTarget = backend::DriverBase::getInstance()->newRenderTarget(
_texture2D ? _texture2D->getBackendTexture() : nullptr,
_depthStencilTexture ? _depthStencilTexture->getBackendTexture() : nullptr,
_depthStencilTexture ? _depthStencilTexture->getBackendTexture() : nullptr);
_renderTarget = backend::DriverBase::getInstance()->newRenderTarget(
_texture2D ? _texture2D->getBackendTexture() : nullptr,
_depthStencilTexture ? _depthStencilTexture->getBackendTexture() : nullptr,
_depthStencilTexture ? _depthStencilTexture->getBackendTexture() : nullptr);
}
_renderTarget->setColorAttachment(_texture2D ? _texture2D->getBackendTexture() : nullptr);
@ -494,7 +503,7 @@ void RenderTexture::onSaveToFile(std::string filename, bool isRGBA, bool forceNo
}
/* get buffer as Image */
void RenderTexture::newImage(std::function<void(RefPtr<Image>)> imageCallback, bool flipImage)
void RenderTexture::newImage(std::function<void(RefPtr<Image>)> imageCallback, bool eglCacheHint)
{
AXASSERT(_pixelFormat == backend::PixelFormat::RGBA8, "only RGBA8888 can be saved as image");
@ -512,7 +521,7 @@ void RenderTexture::newImage(std::function<void(RefPtr<Image>)> imageCallback, b
int savedBufferHeight = (int)s.height;
bool hasPremultipliedAlpha = _texture2D->hasPremultipliedAlpha();
_director->getRenderer()->readPixels(_renderTarget, [=](const backend::PixelBufferDescriptor& pbd) {
auto callback = [hasPremultipliedAlpha, imageCallback](const backend::PixelBufferDescriptor& pbd) {
if (pbd)
{
auto image = utils::makeInstance<Image>(&Image::initWithRawData, pbd._data.getBytes(), pbd._data.getSize(),
@ -521,7 +530,25 @@ void RenderTexture::newImage(std::function<void(RefPtr<Image>)> imageCallback, b
}
else
imageCallback(nullptr);
});
};
#if defined(AX_USE_GL)
if (eglCacheHint)
{
auto colorAttachment = _renderTarget->_color[0].texture;
if (colorAttachment)
{
backend::PixelBufferDescriptor pbd;
static_cast<backend::CommandBufferGL*>(_director->getRenderer()->getCommandBuffer())
->readPixels(_renderTarget, 0, 0, colorAttachment->getWidth(), colorAttachment->getHeight(),
colorAttachment->getWidth() * 4, true, pbd);
callback(pbd);
}
}
else
_director->getRenderer()->readPixels(_renderTarget, callback);
#else
_director->getRenderer()->readPixels(_renderTarget, callback);
#endif
}
void RenderTexture::draw(Renderer* renderer, const Mat4& transform, uint32_t flags)
@ -626,7 +653,6 @@ void RenderTexture::begin()
_director->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
}
Renderer* renderer = _director->getRenderer();
auto* groupCommand = renderer->getNextGroupCommand();
groupCommand->init(_globalZOrder);
@ -637,6 +663,9 @@ void RenderTexture::begin()
beginCommand->init(_globalZOrder);
beginCommand->func = AX_CALLBACK_0(RenderTexture::onBegin, this);
renderer->addCommand(beginCommand);
#if AX_ENABLE_CACHE_TEXTURE_DATA
_cachedTextureDirty = true;
#endif
}
void RenderTexture::end()
@ -684,4 +713,4 @@ void RenderTexture::clearColorAttachment()
renderer->addCommand(afterClearAttachmentCommand);
}
}
} // namespace ax

View File

@ -73,7 +73,11 @@ public:
* @param depthStencilFormat The depthStencil format.
* @param sharedRenderTarget Select whether to use a new or shared render target.
*/
static RenderTexture* create(int w, int h, backend::PixelFormat format, backend::PixelFormat depthStencilFormat, bool sharedRenderTarget = false);
static RenderTexture* create(int w,
int h,
backend::PixelFormat format,
backend::PixelFormat depthStencilFormat,
bool sharedRenderTarget = false);
/** Creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are
* valid.
@ -163,11 +167,11 @@ public:
/* Creates a new Image from with the texture's data.
* Caller is responsible for releasing it by calling delete.
*
* @param flipImage Whether or not to flip image.
* @param eglCacheHint Whether for egl cache, internal use
* @return An image.
* @js NA
*/
void newImage(std::function<void(RefPtr<Image>)> imageCallback, bool flipImage = true);
void newImage(std::function<void(RefPtr<Image>)> imageCallback, bool eglCacheHint = false);
/** Saves the texture into a file using JPEG format. The file will be saved in the Documents folder.
* Returns true if the operation is successful.
@ -384,7 +388,9 @@ protected:
void clearColorAttachment();
void onSaveToFile(std::string fileName, bool isRGBA = true, bool forceNonPMA = false);
#if AX_ENABLE_CACHE_TEXTURE_DATA
bool _cachedTextureDirty = false;
#endif
bool _keepMatrix = false;
Rect _rtTextureRect;
Rect _fullRect;
@ -398,7 +404,7 @@ protected:
backend::RenderTarget* _renderTarget = nullptr;
backend::RenderTarget* _oldRenderTarget = nullptr;
RefPtr<Image> _UITextureImage = nullptr;
RefPtr<Image> _UITextureImage = nullptr;
backend::PixelFormat _pixelFormat = backend::PixelFormat::RGBA8;
Color4F _clearColor;
@ -414,16 +420,16 @@ protected:
*/
Sprite* _sprite = nullptr;
//CallbackCommand _beginCommand;
//CallbackCommand _endCommand;
// CallbackCommand _beginCommand;
// CallbackCommand _endCommand;
//CallbackCommand _beforeClearAttachmentCommand;
//CallbackCommand _afterClearAttachmentCommand;
// CallbackCommand _beforeClearAttachmentCommand;
// CallbackCommand _afterClearAttachmentCommand;
/*this command is used to encapsulate saveToFile,
call saveToFile twice will overwrite this command and callback
and the command and callback will be executed twice.
*/
//CallbackCommand _saveToFileCommand;
// CallbackCommand _saveToFileCommand;
std::function<void(RenderTexture*, std::string_view)> _saveFileCallback = nullptr;
Mat4 _oldTransMatrix, _oldProjMatrix;
@ -436,4 +442,4 @@ private:
// end of textures group
/// @}
}
} // namespace ax

View File

@ -439,7 +439,7 @@ void CommandBufferGL::readPixels(RenderTarget* rt, std::function<void(const Pixe
PixelBufferDescriptor pbd;
if (rt->isDefaultRenderTarget())
{ // read pixels from screen
readPixels(rt, _viewPort.x, _viewPort.y, _viewPort.width, _viewPort.height, _viewPort.width * 4, pbd);
readPixels(rt, _viewPort.x, _viewPort.y, _viewPort.width, _viewPort.height, _viewPort.width * 4, false, pbd);
}
else
{
@ -448,7 +448,7 @@ void CommandBufferGL::readPixels(RenderTarget* rt, std::function<void(const Pixe
if (colorAttachment)
{
readPixels(rt, 0, 0, colorAttachment->getWidth(), colorAttachment->getHeight(),
colorAttachment->getWidth() * 4, pbd);
colorAttachment->getWidth() * 4, false, pbd);
}
}
callback(pbd);
@ -460,6 +460,7 @@ void CommandBufferGL::readPixels(RenderTarget* rt,
uint32_t width,
uint32_t height,
uint32_t bytesPerRow,
bool eglCacheHint,
PixelBufferDescriptor& pbd)
{
auto rtGL = static_cast<RenderTargetGL*>(rt);
@ -474,25 +475,44 @@ void CommandBufferGL::readPixels(RenderTarget* rt,
__gl->bindBuffer(BufferType::PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
auto buffer = (uint8_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, bufferSize, GL_MAP_READ_BIT);
auto buffer_ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, bufferSize, GL_MAP_READ_BIT);
#else
std::unique_ptr<uint8_t[]> bufferStorage(new uint8_t[bufferSize]);
auto buffer = bufferStorage.get();
memset(buffer, 0, bufferSize);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
axstd::byte_buffer buffer(static_cast<size_t>(bufferSize), 0);
auto buffer_ptr = buffer.data();
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer_ptr);
#endif
uint8_t* wptr = nullptr;
if (buffer && (wptr = pbd._data.resize(bufferSize)))
CHECK_GL_ERROR_DEBUG();
if (buffer_ptr)
{
auto rptr = buffer + (height - 1) * bytesPerRow;
for (int row = 0; row < height; ++row)
if (!eglCacheHint)
{
memcpy(wptr, rptr, bytesPerRow);
wptr += bytesPerRow;
rptr -= bytesPerRow;
// we need to flip the buffer vertically to match our API
uint8_t* wptr = nullptr;
if (wptr = pbd._data.resize(bufferSize))
{
auto rptr = buffer_ptr + (height - 1) * bytesPerRow;
for (int row = 0; row < height; ++row)
{
memcpy(wptr, rptr, bytesPerRow);
wptr += bytesPerRow;
rptr -= bytesPerRow;
}
pbd._width = width;
pbd._height = height;
}
}
else
{
// for cache for restore on EGL context resume, don't need flip
pbd._width = width;
pbd._height = height;
#if AX_GLES_PROFILE != 200
pbd._data.copy(buffer_ptr, static_cast<ssize_t>(bufferSize));
#else
static_cast<axstd::byte_buffer&>(pbd._data).swap(buffer);
#endif
}
pbd._width = width;
pbd._height = height;
}
#if AX_GLES_PROFILE != 200
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);

View File

@ -204,13 +204,17 @@ public:
*/
void readPixels(RenderTarget* rt, std::function<void(const PixelBufferDescriptor&)> callback) override;
protected:
/**
* For internal use only
*/
void readPixels(RenderTarget* rt,
int x,
int y,
uint32_t width,
uint32_t height,
uint32_t bytesPerRow,
bool eglCacheHint,
PixelBufferDescriptor& pbd);
protected: