diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index e9a4934d..7ca9b980 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -2786,13 +2786,130 @@ namespace MobileGL::MG_Backend::DirectGLES { return true; } + static Bool ReadPixelsDepthFloatViaUnsignedInt(GLint x, GLint y, GLsizei width, GLsizei height, void* pixels) { + if (width <= 0 || height <= 0) { + return true; + } + + Vector raw(static_cast(width) * static_cast(height)); + GLint prevPixelPackBuffer = 0; + g_GLESFuncs.glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &prevPixelPackBuffer); + g_GLESFuncs.glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_ALIGNMENT, 1); + g_GLESFuncs.glPixelStorei(GL_PACK_ROW_LENGTH, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_SKIP_ROWS, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + g_GLESFuncs.glReadPixels(x, y, width, height, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, raw.data()); + const GLenum readError = g_GLESFuncs.glGetError(); + g_GLESFuncs.glBindBuffer(GL_PIXEL_PACK_BUFFER, static_cast(prevPixelPackBuffer)); + if (readError != GL_NO_ERROR) { + MGLOG_E("ReadPixels: depth GL_FLOAT fallback read failed: %s", + MG_Util::ConvertGLEnumToString(readError).c_str()); + return true; + } + + const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false); + const SizeT rowPixels = static_cast(packParams.RowLength > 0 ? packParams.RowLength : width); + const SizeT dstPixelBytes = sizeof(Float); + const SizeT dstRowStride = AlignPixelRow(rowPixels * dstPixelBytes, packParams.Alignment); + const SizeT dstOffset = static_cast(std::max(packParams.SkipRows, 0)) * dstRowStride + + static_cast(std::max(packParams.SkipPixels, 0)) * dstPixelBytes; + const SizeT packedSize = dstOffset + static_cast(height - 1) * dstRowStride + + static_cast(width) * dstPixelBytes; + Vector packed(packedSize, 0); + + for (GLsizei row = 0; row < height; ++row) { + const Uint32* srcRow = raw.data() + static_cast(row) * static_cast(width); + auto* dstRow = reinterpret_cast(packed.data() + dstOffset + + static_cast(row) * dstRowStride); + for (GLsizei col = 0; col < width; ++col) { + // TODO: preserve native depth precision when GLES exposes float depth readback directly. + dstRow[col] = static_cast(static_cast(srcRow[col]) / 4294967295.0); + } + } + + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + if (pixelPackBufferObject) { + const SizeT pboOffset = reinterpret_cast(pixels); + if (pboOffset + packed.size() > pixelPackBufferObject->GetSize()) { + MGLOG_E("ReadPixels: depth GL_FLOAT fallback PBO is too small"); + return true; + } + pixelPackBufferObject->UploadSubData({packed.data(), packed.size()}, pboOffset); + pixelPackBufferObject->ClearDirty(); + } else if (pixels != nullptr && !packed.empty()) { + Memcpy(pixels, packed.data(), packed.size()); + } + return true; + } + + static Bool ReadPixelsStencilUintViaUnsignedByte(GLint x, GLint y, GLsizei width, GLsizei height, void* pixels) { + if (width <= 0 || height <= 0) { + return true; + } + + Vector raw(static_cast(width) * static_cast(height)); + GLint prevPixelPackBuffer = 0; + g_GLESFuncs.glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &prevPixelPackBuffer); + g_GLESFuncs.glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_ALIGNMENT, 1); + g_GLESFuncs.glPixelStorei(GL_PACK_ROW_LENGTH, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_SKIP_ROWS, 0); + g_GLESFuncs.glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + g_GLESFuncs.glReadPixels(x, y, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, raw.data()); + const GLenum readError = g_GLESFuncs.glGetError(); + g_GLESFuncs.glBindBuffer(GL_PIXEL_PACK_BUFFER, static_cast(prevPixelPackBuffer)); + if (readError != GL_NO_ERROR) { + MGLOG_E("ReadPixels: stencil GL_UNSIGNED_INT fallback read failed: %s", + MG_Util::ConvertGLEnumToString(readError).c_str()); + return true; + } + + const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false); + const SizeT rowPixels = static_cast(packParams.RowLength > 0 ? packParams.RowLength : width); + const SizeT dstPixelBytes = sizeof(Uint32); + const SizeT dstRowStride = AlignPixelRow(rowPixels * dstPixelBytes, packParams.Alignment); + const SizeT dstOffset = static_cast(std::max(packParams.SkipRows, 0)) * dstRowStride + + static_cast(std::max(packParams.SkipPixels, 0)) * dstPixelBytes; + const SizeT packedSize = dstOffset + static_cast(height - 1) * dstRowStride + + static_cast(width) * dstPixelBytes; + Vector packed(packedSize, 0); + + for (GLsizei row = 0; row < height; ++row) { + const Uint8* srcRow = raw.data() + static_cast(row) * static_cast(width); + auto* dstRow = reinterpret_cast(packed.data() + dstOffset + + static_cast(row) * dstRowStride); + for (GLsizei col = 0; col < width; ++col) { + // TODO: switch to native uint stencil readback if the GLES backend exposes it. + dstRow[col] = srcRow[col]; + } + } + + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + if (pixelPackBufferObject) { + const SizeT pboOffset = reinterpret_cast(pixels); + if (pboOffset + packed.size() > pixelPackBufferObject->GetSize()) { + MGLOG_E("ReadPixels: stencil GL_UNSIGNED_INT fallback PBO is too small"); + return true; + } + pixelPackBufferObject->UploadSubData({packed.data(), packed.size()}, pboOffset); + pixelPackBufferObject->ClearDirty(); + } else if (pixels != nullptr && !packed.empty()) { + Memcpy(pixels, packed.data(), packed.size()); + } + return true; + } + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { MGLOG_D("ReadPixels: x=%d y=%d w=%d h=%d format=%s type=%s pixels=%p", x, y, width, height, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_RED || - format == GL_RED_INTEGER, - "Only GL_RGBA, GL_RGBA_INTEGER, GL_RED and GL_RED_INTEGER are supported currently, " + format == GL_RED_INTEGER || format == GL_DEPTH_COMPONENT || format == GL_STENCIL_INDEX, + "Only GL_RGBA, GL_RGBA_INTEGER, GL_RED, GL_RED_INTEGER, GL_DEPTH_COMPONENT and " + "GL_STENCIL_INDEX are supported currently, " "while requested %s.", MG_Util::ConvertGLEnumToString(format).c_str()); MOBILEGL_ASSERT(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV || @@ -2820,6 +2937,16 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_E("ReadPixels: bound READ FBO is not complete"); return; } + if (format == GL_DEPTH_COMPONENT && type == GL_FLOAT && + ReadPixelsDepthFloatViaUnsignedInt(x, y, width, height, pixels)) { + MGLOG_D("ReadPixels: finished via depth GL_FLOAT fallback"); + return; + } + if (format == GL_STENCIL_INDEX && type == GL_UNSIGNED_INT && + ReadPixelsStencilUintViaUnsignedByte(x, y, width, height, pixels)) { + MGLOG_D("ReadPixels: finished via stencil GL_UNSIGNED_INT fallback"); + return; + } if (type == GL_FLOAT && ReadPixelsFloatViaUnsignedByte(x, y, width, height, format, pixels)) { MGLOG_D("ReadPixels: finished via GL_FLOAT fallback"); return;