[Optimization] (TextureState): block memcpy

This commit is contained in:
2025-05-08 16:07:17 +08:00
parent d33fb15ada
commit 40cf5a1ebb
3 changed files with 140 additions and 70 deletions
@@ -416,12 +416,7 @@ namespace MG_GL::GL {
case GL_TEXTURE_2D: { case GL_TEXTURE_2D: {
GLenum internalFormat = 0, type = 0, format = 0; GLenum internalFormat = 0, type = 0, format = 0;
NormalizePixelFormat(mip.internalFormat, mip.type, mip.format, &internalFormat, &type, &format); NormalizePixelFormat(mip.internalFormat, mip.type, mip.format, &internalFormat, &type, &format);
//
// GLenum type = mip.type;
// // Mali cannot use GL_FLOAT as depth format
// if (mip.internalFormat == GL_DEPTH_COMPONENT) {
// type = GL_UNSIGNED_INT;
// }
CallAndCheck(::GLES::glTexImage2D( CallAndCheck(::GLES::glTexImage2D(
target, level, internalFormat, target, level, internalFormat,
mip.width, mip.height, 0, mip.width, mip.height, 0,
@@ -446,6 +441,8 @@ namespace MG_GL::GL {
if (!s_textureLevelUploaded[mgTexId][level]) { if (!s_textureLevelUploaded[mgTexId][level]) {
bool levelInitialized = s_textureLevelUploaded[mgTexId].count(level); bool levelInitialized = s_textureLevelUploaded[mgTexId].count(level);
const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr; const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr;
GLenum internalFormat = 0, type = 0, format = 0;
NormalizePixelFormat(mip.internalFormat, mip.type, mip.format, &internalFormat, &type, &format);
switch (target) { switch (target) {
case GL_TEXTURE_2D: case GL_TEXTURE_2D:
@@ -454,15 +451,15 @@ namespace MG_GL::GL {
target, level, target, level,
0, 0, 0, 0,
mip.width, mip.height, mip.width, mip.height,
mip.format, mip.type, data format, type, data
);) );)
s_textureLevelUploaded[mgTexId][level] = true; s_textureLevelUploaded[mgTexId][level] = true;
MG_Util::Debug::LogD("Updated texture %u level %d with glTexSubImage2D", mgTexId, level); MG_Util::Debug::LogD("Updated texture %u level %d with glTexSubImage2D", mgTexId, level);
} else { } else {
CallAndCheck(::GLES::glTexImage2D( CallAndCheck(::GLES::glTexImage2D(
target, level, mip.internalFormat, target, level, internalFormat,
mip.width, mip.height, 0, mip.width, mip.height, 0,
mip.format, mip.type, data format, type, data
);) );)
s_textureLevelUploaded[mgTexId][level] = true; s_textureLevelUploaded[mgTexId][level] = true;
MG_Util::Debug::LogD("Initialized texture %u level %d with glTexImage2D", mgTexId, level); MG_Util::Debug::LogD("Initialized texture %u level %d with glTexImage2D", mgTexId, level);
+123 -55
View File
@@ -11,7 +11,7 @@
// TextureObject // TextureObject
bool TextureObject::IsImmutable() const { bool TextureObject::IsImmutable() const {
bool immutable = params.texPropertiesInt.count(GL_TEXTURE_IMMUTABLE_FORMAT); bool immutable = params.texPropertiesInt.find(GL_TEXTURE_IMMUTABLE_FORMAT) != params.texPropertiesInt.end();
MG_Util::Debug::LogD("MG_State: Texture: TextureObject::IsImmutable returns %d", immutable); MG_Util::Debug::LogD("MG_State: Texture: TextureObject::IsImmutable returns %d", immutable);
return immutable; return immutable;
} }
@@ -54,7 +54,7 @@ bool TextureState::IsTextureGenerated(GLuint texture) {
if (!isValidTexture) { if (!isValidTexture) {
MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated invalid texture %d", texture); MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated invalid texture %d", texture);
} else { } else {
generated = textures.at(texture).generated; generated = textures[texture].generated;
if (!generated) { if (!generated) {
MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated texture %d not generated", texture, generated); MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated texture %d not generated", texture, generated);
} }
@@ -101,12 +101,12 @@ GLenum TextureState::CreateN(GLsizei n, GLuint* textures) {
for (GLsizei i = 0; i < n; ++i) { for (GLsizei i = 0; i < n; ++i) {
GLuint id = 0; GLuint id = 0;
if (!freeIDs_.empty()) { if (!freeID_.empty()) {
id = *freeIDs_.begin(); id = freeID_.back();
freeIDs_.erase(freeIDs_.begin()); freeID_.pop_back();
MG_Util::Debug::LogD("MG_State: Texture: CreateN reusing free id=%u", id); MG_Util::Debug::LogD("MG_State: Texture: CreateN reusing free id=%u", id);
} else { } else {
id = ++lastUsedID_; id = lastUsedID_++;
MG_Util::Debug::LogD("MG_State: Texture: CreateN new id=%u", id); MG_Util::Debug::LogD("MG_State: Texture: CreateN new id=%u", id);
} }
TextureObject obj; TextureObject obj;
@@ -272,13 +272,19 @@ GLenum TextureState::Upload2D(GLenum target, GLint level, GLint internalFormat,
GLuint boundTex = textureUnits_[activeTextureUnit_].GetBoundTexture(target); GLuint boundTex = textureUnits_[activeTextureUnit_].GetBoundTexture(target);
TextureObject& tex = textures[boundTex]; TextureObject& tex = textures[boundTex];
TextureParams::MipmapLevel mip{}; // TextureParams::MipmapLevel mip{};
auto& mip = tex.params.mipmapData[level];
mip.width = width; mip.width = width;
mip.height = height; mip.height = height;
mip.internalFormat = internalFormat; mip.internalFormat = internalFormat;
mip.format = format; mip.format = format;
mip.type = type; mip.type = type;
if (data != nullptr) {
if (data == nullptr) {
mip.hasData = false;
MG_Util::Debug::LogD("MG_State: Texture: Upload2D data pointer is null");
} else {
GLint unpackSwapBytes = GetUnpackParam_(GL_UNPACK_SWAP_BYTES); GLint unpackSwapBytes = GetUnpackParam_(GL_UNPACK_SWAP_BYTES);
GLint unpackLSBFirst = GetUnpackParam_(GL_UNPACK_LSB_FIRST); GLint unpackLSBFirst = GetUnpackParam_(GL_UNPACK_LSB_FIRST);
GLint unpackSkipPixels = GetUnpackParam_(GL_UNPACK_SKIP_PIXELS); GLint unpackSkipPixels = GetUnpackParam_(GL_UNPACK_SKIP_PIXELS);
@@ -288,47 +294,69 @@ GLenum TextureState::Upload2D(GLenum target, GLint level, GLint internalFormat,
GLint unpackImageHeight = GetUnpackParam_(GL_UNPACK_IMAGE_HEIGHT); GLint unpackImageHeight = GetUnpackParam_(GL_UNPACK_IMAGE_HEIGHT);
GLint unpackSkipImages = GetUnpackParam_(GL_UNPACK_SKIP_IMAGES); GLint unpackSkipImages = GetUnpackParam_(GL_UNPACK_SKIP_IMAGES);
GLsizei rowLength = (unpackRowLength > 0) ? unpackRowLength : width; bool isDefaultUnpack = (unpackSwapBytes == 0) && (unpackLSBFirst == 0) &&
(unpackSkipPixels == 0) && (unpackSkipRows == 0) &&
(unpackRowLength == 0) && (unpackAlignment == 4) &&
(unpackImageHeight == 0) && (unpackSkipImages == 0);
size_t bytesPerPixel = CalculateBytesPerPixel_(format, type); size_t bytesPerPixel = CalculateBytesPerPixel_(format, type);
size_t componentSize = GetComponentSize_(type); size_t componentSize = GetComponentSize_(type);
size_t srcRowSize = rowLength * bytesPerPixel;
size_t srcRowStride = (srcRowSize + unpackAlignment - 1) & ~(unpackAlignment - 1);
size_t srcImageStride = (unpackImageHeight > 0) ?
srcRowStride * unpackImageHeight :
srcRowStride * height;
const GLubyte* srcData = static_cast<const GLubyte*>(data);
srcData += unpackSkipImages * srcImageStride;
srcData += unpackSkipRows * srcRowStride;
srcData += unpackSkipPixels * bytesPerPixel;
size_t dstRowStride = width * bytesPerPixel; size_t dstRowStride = width * bytesPerPixel;
size_t dstSize = dstRowStride * height; size_t dstSize = dstRowStride * height;
mip.pixelData.resize(dstSize); mip.pixelData.resize(dstSize);
GLubyte* dstData = mip.pixelData.data(); GLubyte *dstData = mip.pixelData.data();
for (GLsizei y = 0; y < height; ++y) { if (isDefaultUnpack) {
const GLubyte* srcRow = srcData + y * srcRowStride; const GLubyte *srcData = static_cast<const GLubyte *>(data);
GLubyte* dstRow = dstData + y * dstRowStride; size_t srcRowSize = width * bytesPerPixel;
size_t srcRowStride = (srcRowSize + unpackAlignment - 1) & ~(unpackAlignment - 1);
if (unpackSwapBytes) { if (srcRowStride == dstRowStride) {
SwapBytesForTexture_(format, type, srcRow, dstRow, width); memcpy(dstData, srcData, dstSize);
MG_Util::Debug::LogD(
"MG_State: Texture: Upload2D uploaded %zu bytes with fast path - block memcpy", dstSize);
} else {
for (GLsizei y = 0; y < height; ++y) {
const GLubyte *srcRow = srcData + y * srcRowStride;
GLubyte *dstRow = dstData + y * dstRowStride;
memcpy(dstRow, srcRow, srcRowSize);
}
MG_Util::Debug::LogD(
"MG_State: Texture: Upload2D uploaded %zu bytes with fast path - row memcpy", dstSize);
} }
else if (unpackLSBFirst && componentSize == 1) { } else {
ReverseBitOrder_(srcRow, dstRow, width * bytesPerPixel); GLsizei rowLength = (unpackRowLength > 0) ? unpackRowLength : width;
}
else { size_t srcRowSize = rowLength * bytesPerPixel;
memcpy(dstRow, srcRow, width * bytesPerPixel); size_t srcRowStride = (srcRowSize + unpackAlignment - 1) & ~(unpackAlignment - 1);
size_t srcImageStride = (unpackImageHeight > 0) ?
srcRowStride * unpackImageHeight :
srcRowStride * height;
const GLubyte *srcData = static_cast<const GLubyte *>(data);
srcData += unpackSkipImages * srcImageStride;
srcData += unpackSkipRows * srcRowStride;
srcData += unpackSkipPixels * bytesPerPixel;
for (GLsizei y = 0; y < height; ++y) {
const GLubyte *srcRow = srcData + y * srcRowStride;
GLubyte *dstRow = dstData + y * dstRowStride;
if (unpackSwapBytes) {
SwapBytesForTexture_(format, type, srcRow, dstRow, width);
} else if (unpackLSBFirst && componentSize == 1) {
ReverseBitOrder_(srcRow, dstRow, width * bytesPerPixel);
} else {
memcpy(dstRow, srcRow, width * bytesPerPixel);
}
} }
mip.hasData = true;
MG_Util::Debug::LogD(
"MG_State: Texture: Upload2D uploaded %zu bytes with unpack params", dstSize);
} }
mip.hasData = true;
MG_Util::Debug::LogD("MG_State: Texture: Upload2D uploaded %zu bytes with unpack params", dstSize);
} else {
mip.hasData = false;
MG_Util::Debug::LogD("MG_State: Texture: Upload2D data pointer is null");
} }
tex.params.mipmapData[level] = mip; // tex.params.mipmapData[level] = mip;
return GL_NO_ERROR; return GL_NO_ERROR;
} }
@@ -364,7 +392,7 @@ GLenum TextureState::UpdateRegion2D(GLenum target, GLint level, GLint xoffset,
const size_t bytesPerPixel = CalculateBytesPerPixel_(format, type); const size_t bytesPerPixel = CalculateBytesPerPixel_(format, type);
const size_t srcSize = CalculatePixelDataSize_(format, type, width, height); const size_t srcSize = CalculatePixelDataSize_(format, type, width, height);
if (bytesPerPixel == 0) { if (bytesPerPixel == 0) {
MG_Util::Debug::LogE("MG_State: Texture: Invalid format/type combination"); MG_Util::Debug::LogE("MG_State: Texture: Invalid format/type combination");
return GL_INVALID_ENUM; return GL_INVALID_ENUM;
@@ -402,26 +430,66 @@ GLenum TextureState::UpdateRegion2D(GLenum target, GLint level, GLint xoffset,
MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D srcData=%p, dstData=%p", srcData, dstData); MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D srcData=%p, dstData=%p", srcData, dstData);
MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D bytesPerPixel=%zu", bytesPerPixel); MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D bytesPerPixel=%zu", bytesPerPixel);
for (GLsizei y = 0; y < height; ++y) { // Check if fast path is applicable
const GLubyte* srcRow = srcData + y * srcRowStride; const bool isDefaultUnpack = (unpackSwapBytes == 0) && (unpackLSBFirst == 0) &&
GLubyte* dstRow = dstData + y * dstRowStride; (unpackRowLength == 0) && (unpackAlignment == 4) &&
(unpackSkipPixels == 0) && (unpackSkipRows == 0) &&
(unpackImageHeight == 0) && (unpackSkipImages == 0);
if (unpackSwapBytes) { if (isDefaultUnpack) {
for (GLsizei x = 0; x < width; ++x) { const size_t copySize = width * bytesPerPixel;
const GLubyte* srcPixel = srcRow + x * bytesPerPixel;
GLubyte* dstPixel = dstRow + x * bytesPerPixel; if (srcRowStride == dstRowStride) {
SwapPixelBytes_(format, type, srcPixel, dstPixel); MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D block memcpy(dst=%p, src=%p, size=%zu) called.", dstData, srcData, width * bytesPerPixel);
} memcpy(dstData, srcData, copySize * height);
} else if (unpackLSBFirst && GetComponentSize_(type) == 1) {
for (size_t i = 0; i < width * bytesPerPixel; ++i) {
dstRow[i] = ReverseBits_(srcRow[i]);
}
} else { } else {
MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D memcpy(dst=%p, src=%p, size=%zu) called.", dstRow, srcRow, width * bytesPerPixel); for (GLsizei y = 0; y < height; ++y) {
memcpy(dstRow, srcRow, width * bytesPerPixel); const GLubyte* srcRow = srcData + y * srcRowStride;
GLubyte* dstRow = dstData + y * dstRowStride;
memcpy(dstRow, srcRow, copySize);
MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D row memcpy(dst=%p, src=%p, size=%zu) called.", dstRow, srcRow, width * bytesPerPixel);
}
}
} else {
const size_t componentSize = GetComponentSize_(type);
for (GLsizei y = 0; y < height; ++y) {
const GLubyte* srcRow = srcData + y * srcRowStride;
GLubyte* dstRow = dstData + y * dstRowStride;
if (unpackSwapBytes) {
for (GLsizei x = 0; x < width; ++x) {
SwapPixelBytes_(format, type, srcRow + x*bytesPerPixel, dstRow + x*bytesPerPixel);
}
} else if (unpackLSBFirst && componentSize == 1) {
for (size_t i = 0; i < width * bytesPerPixel; ++i) {
dstRow[i] = ReverseBits_(srcRow[i]);
}
} else {
memcpy(dstRow, srcRow, width * bytesPerPixel);
}
} }
} }
// for (GLsizei y = 0; y < height; ++y) {
// const GLubyte* srcRow = srcData + y * srcRowStride;
// GLubyte* dstRow = dstData + y * dstRowStride;
//
// if (unpackSwapBytes) {
// for (GLsizei x = 0; x < width; ++x) {
// const GLubyte* srcPixel = srcRow + x * bytesPerPixel;
// GLubyte* dstPixel = dstRow + x * bytesPerPixel;
// SwapPixelBytes_(format, type, srcPixel, dstPixel);
// }
// } else if (unpackLSBFirst && GetComponentSize_(type) == 1) {
// for (size_t i = 0; i < width * bytesPerPixel; ++i) {
// dstRow[i] = ReverseBits_(srcRow[i]);
// }
// } else {
// MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D memcpy(dst=%p, src=%p, size=%zu) called.", dstRow, srcRow, width * bytesPerPixel);
// memcpy(dstRow, srcRow, width * bytesPerPixel);
// }
// }
mip.hasData = true; mip.hasData = true;
MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D succeeded"); MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D succeeded");
return GL_NO_ERROR; return GL_NO_ERROR;
@@ -541,7 +609,7 @@ GLenum TextureState::Delete(GLuint texture) {
if (it != this->textures.end()) { if (it != this->textures.end()) {
InvalidateTextureInAllUnits_(texture); InvalidateTextureInAllUnits_(texture);
this->textures.erase(it); this->textures.erase(it);
freeIDs_.insert(texture); freeID_.emplace_back(texture);
MG_Util::Debug::LogD("MG_State: Texture: Delete succeeded for texture=%u", texture); MG_Util::Debug::LogD("MG_State: Texture: Delete succeeded for texture=%u", texture);
} else { } else {
MG_Util::Debug::LogW("MG_State: Texture: Delete texture %u not found", texture); MG_Util::Debug::LogW("MG_State: Texture: Delete texture %u not found", texture);
@@ -563,7 +631,7 @@ GLenum TextureState::DeleteN(GLsizei n, const GLuint* textures) {
if (it != this->textures.end()) { if (it != this->textures.end()) {
InvalidateTextureInAllUnits_(id); InvalidateTextureInAllUnits_(id);
this->textures.erase(it); this->textures.erase(it);
freeIDs_.insert(id); freeID_.emplace_back(id);
MG_Util::Debug::LogD("MG_State: Texture: DeleteN deleted texture=%u", id); MG_Util::Debug::LogD("MG_State: Texture: DeleteN deleted texture=%u", id);
} else { } else {
MG_Util::Debug::LogW("MG_State: Texture: DeleteN texture %u not found", id); MG_Util::Debug::LogW("MG_State: Texture: DeleteN texture %u not found", id);
+11 -6
View File
@@ -19,8 +19,10 @@ struct ComponentSizes {
}; };
struct TextureParams { struct TextureParams {
std::unordered_map<GLenum, GLfloat> texPropertiesFloat; template <typename K, typename V>
std::unordered_map<GLenum, GLint> texPropertiesInt; using unordered_map = ankerl::unordered_dense::map<K, V>;
unordered_map<GLenum, GLfloat> texPropertiesFloat;
unordered_map<GLenum, GLint> texPropertiesInt;
struct MipmapLevel { struct MipmapLevel {
GLsizei width = 0; GLsizei width = 0;
GLsizei height = 0; GLsizei height = 0;
@@ -30,7 +32,7 @@ struct TextureParams {
std::vector<GLubyte> pixelData; std::vector<GLubyte> pixelData;
bool hasData = false; bool hasData = false;
}; };
std::unordered_map<GLint, MipmapLevel> mipmapData; unordered_map<GLint, MipmapLevel> mipmapData;
}; };
class TextureObject { class TextureObject {
@@ -44,11 +46,13 @@ public:
}; };
class TextureUnitState { class TextureUnitState {
template <typename K, typename V>
using unordered_map = ankerl::unordered_dense::map<K, V>;
private: private:
GLenum activeTarget = GL_TEXTURE_2D; GLenum activeTarget = GL_TEXTURE_2D;
public: public:
std::unordered_map<GLenum, GLuint> boundTextures; unordered_map<GLenum, GLuint> boundTextures;
void Bind(GLenum target, GLuint texture); void Bind(GLenum target, GLuint texture);
GLuint GetBoundTexture(GLenum target); GLuint GetBoundTexture(GLenum target);
}; };
@@ -59,8 +63,9 @@ class TextureState {
private: private:
GLuint activeTextureUnit_ = 0; GLuint activeTextureUnit_ = 0;
GLuint lastUsedID_ = 0; GLuint lastUsedID_ = 1;
std::unordered_set<GLuint> freeIDs_; // std::unordered_set<GLuint> freeIDs_;
std::vector<GLuint> freeID_;
unordered_map<GLenum, TextureObject> proxyTextures_; unordered_map<GLenum, TextureObject> proxyTextures_;