diff --git a/CMakeLists.txt b/CMakeLists.txt index cdad1306..95f24a2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,6 +346,18 @@ target_compile_definitions(${CMAKE_PROJECT_NAME} MOBILEGL_LOG_ACTIVE_LEVEL=${MOBILEGL_LOG_ACTIVE_LEVEL} ) +if(UNIX AND NOT APPLE AND NOT ANDROID) + foreach(MOBILEGL_LOADER_ALIAS + libEGL.so libEGL.so.1) + add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + "$" + "$/${MOBILEGL_LOADER_ALIAS}" + COMMENT "Creating ${MOBILEGL_LOADER_ALIAS} alias for Linux GL/EGL loaders" + ) + endforeach() +endif() + if(NOT ANDROID) add_library(${CMAKE_PROJECT_NAME}_s STATIC ${SOURCE_FILES} diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index e08c10b0..efe62139 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -906,7 +906,9 @@ namespace MobileGL::MG_Backend::DirectGLES { m_dynamicParameters.MaxTextureBufferSize = m_GLESCapabilities.MaxTextureBufferSize; m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings; m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize; - m_dynamicParameters.MaxImageUnits = m_GLESCapabilities.MaxImageUnits; + const Int maxSupportedTextureUnits = + static_cast(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); + m_dynamicParameters.MaxImageUnits = std::min(m_GLESCapabilities.MaxImageUnits, maxSupportedTextureUnits); m_dynamicParameters.MaxCombinedImageUniforms = m_GLESCapabilities.MaxCombinedImageUniforms; m_dynamicParameters.MaxComputeImageUniforms = m_GLESCapabilities.MaxComputeImageUniforms; m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 7a9b77df..e9a4934d 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -2698,6 +2698,94 @@ namespace MobileGL::MG_Backend::DirectGLES { } }; + static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) { + const SizeT resolvedAlignment = static_cast(std::max(alignment, 1)); + return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1); + } + + static Int GetFloatReadbackChannelCount(GLenum format) { + switch (format) { + case GL_RED: + return 1; + case GL_RGBA: + return 4; + default: + return 0; + } + } + + static Bool ReadPixelsFloatViaUnsignedByte(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + void* pixels) { + if (width <= 0 || height <= 0) { + return true; + } + const Int dstChannels = GetFloatReadbackChannelCount(format); + if (dstChannels == 0) { + return false; + } + + const GLenum readFormat = format == GL_RED ? GL_RED : GL_RGBA; + const Int readChannels = format == GL_RED ? 1 : 4; + Vector raw(static_cast(width) * static_cast(height) * + static_cast(readChannels)); + + 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, readFormat, 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: 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 = static_cast(dstChannels) * 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 Uint8* srcRow = raw.data() + static_cast(row) * static_cast(width) * + static_cast(readChannels); + auto* dstRow = reinterpret_cast(packed.data() + dstOffset + + static_cast(row) * dstRowStride); + for (GLsizei col = 0; col < width; ++col) { + const Uint8* src = srcRow + static_cast(col) * static_cast(readChannels); + Float* dst = dstRow + static_cast(col) * static_cast(dstChannels); + // TODO: extend readback packing to all desktop GL read formats instead of only normalized RED/RGBA. + for (Int component = 0; component < dstChannels; ++component) { + dst[component] = static_cast(src[component]) / 255.0f; + } + } + } + + 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: 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; + } + 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); @@ -2732,6 +2820,10 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_E("ReadPixels: bound READ FBO is not complete"); return; } + if (type == GL_FLOAT && ReadPixelsFloatViaUnsignedByte(x, y, width, height, format, pixels)) { + MGLOG_D("ReadPixels: finished via GL_FLOAT fallback"); + return; + } // Handle PBO auto& pixelPackBufferObject = diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 3faa2571..1bb45b80 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -690,7 +690,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_dynamicParameters.MaxTextureBufferSize = m_vulkanCaps.MaxTextureBufferSize; m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings; m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize; - m_dynamicParameters.MaxImageUnits = m_vulkanCaps.MaxImageUnits; + m_dynamicParameters.MaxImageUnits = std::min(m_vulkanCaps.MaxImageUnits, maxSupportedTextureUnits); m_dynamicParameters.MaxCombinedImageUniforms = m_vulkanCaps.MaxCombinedImageUniforms; m_dynamicParameters.MaxComputeImageUniforms = m_vulkanCaps.MaxComputeImageUniforms; const Int maxSupportedDrawBuffers = diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 4cd9e419..af01c1bd 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -1041,38 +1041,46 @@ void main() { } static Bool EnsureGenerateMipmapStorageAllocated(::MobileGL::MG_State::GLState::TextureObjectMipmap& texture, - ::MobileGL::TextureUploadTarget uploadTarget, Uint32 baseMipLevel) { const Uint32 existingMipLevelCount = static_cast(texture.GetMipmapLevelCount()); if (existingMipLevelCount <= baseMipLevel) { return false; } - const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, baseMipLevel); - const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, baseMipLevel); - if (baseTexelSize.x() <= 0 || baseTexelSize.y() <= 0 || baseTexelSize.z() <= 0 || baseByteSize == 0) { + const auto& uploadTargets = texture.GetUploadTargets(); + if (uploadTargets.empty()) { return false; } - const SizeT baseTexelCount = static_cast(baseTexelSize.x()) * static_cast(baseTexelSize.y()) * - static_cast(baseTexelSize.z()); - if (baseTexelCount == 0 || (baseByteSize % baseTexelCount) != 0) { - return false; - } + for (const auto uploadTarget : uploadTargets) { + const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, baseMipLevel); + const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, baseMipLevel); + if (baseTexelSize.x() <= 0 || baseTexelSize.y() <= 0 || baseTexelSize.z() <= 0 || + baseByteSize == 0) { + return false; + } - const SizeT bytesPerTexel = baseByteSize / baseTexelCount; - const Uint32 requiredMipLevelCount = baseMipLevel + ComputeFullMipLevelCount(baseTexelSize); - if (existingMipLevelCount >= requiredMipLevelCount) { - return true; - } + const SizeT baseTexelCount = static_cast(baseTexelSize.x()) * + static_cast(baseTexelSize.y()) * + static_cast(baseTexelSize.z()); + if (baseTexelCount == 0 || (baseByteSize % baseTexelCount) != 0) { + return false; + } - for (Uint32 level = existingMipLevelCount; level < requiredMipLevelCount; ++level) { - const IntVec3 levelTexelSize = ComputeMipTexelSize(baseTexelSize, level - baseMipLevel); - const SizeT levelByteSize = bytesPerTexel * static_cast(levelTexelSize.x()) * - static_cast(levelTexelSize.y()) * - static_cast(levelTexelSize.z()); - texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize}); - texture.MarkStorageDirty(uploadTarget, level, false); + const SizeT bytesPerTexel = baseByteSize / baseTexelCount; + const Uint32 requiredMipLevelCount = baseMipLevel + ComputeFullMipLevelCount(baseTexelSize); + if (existingMipLevelCount >= requiredMipLevelCount) { + continue; + } + + for (Uint32 level = existingMipLevelCount; level < requiredMipLevelCount; ++level) { + const IntVec3 levelTexelSize = ComputeMipTexelSize(baseTexelSize, level - baseMipLevel); + const SizeT levelByteSize = bytesPerTexel * static_cast(levelTexelSize.x()) * + static_cast(levelTexelSize.y()) * + static_cast(levelTexelSize.z()); + texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize}); + texture.MarkStorageDirty(uploadTarget, level, false); + } } return true; } @@ -1696,12 +1704,47 @@ void main() { } } + static void StoreReadbackPixelFloat(const Uint8* src, Bool srcIsBgra, GLenum dstFormat, Float* dst) { + const Float r = static_cast(srcIsBgra ? src[2] : src[0]) / 255.0f; + const Float g = static_cast(src[1]) / 255.0f; + const Float b = static_cast(srcIsBgra ? src[0] : src[2]) / 255.0f; + const Float a = static_cast(src[3]) / 255.0f; + + // TODO: extend readback packing to integer/depth formats instead of only normalized color formats. + switch (dstFormat) { + case GL_RGB: + dst[0] = r; + dst[1] = g; + dst[2] = b; + break; + case GL_BGR: + dst[0] = b; + dst[1] = g; + dst[2] = r; + break; + case GL_RGBA: + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst[3] = a; + break; + case GL_BGRA: + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = a; + break; + default: + break; + } + } + static Bool PackReadbackToClientOrPbo(const Uint8* srcPixels, VkFormat srcFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { if (width <= 0 || height <= 0) { return true; } - if (type != GL_UNSIGNED_BYTE) { + if (type != GL_UNSIGNED_BYTE && type != GL_FLOAT) { MGLOG_E("DirectVulkan readback skipped: unsupported type=0x%x", type); return false; } @@ -1713,15 +1756,16 @@ void main() { } const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false); + const SizeT dstComponentBytes = type == GL_FLOAT ? sizeof(Float) : sizeof(Uint8); const SizeT rowPixels = static_cast(packParams.RowLength > 0 ? packParams.RowLength : width); - const SizeT dstRowStride = AlignPixelRow(rowPixels * static_cast(dstChannels), + const SizeT dstRowStride = AlignPixelRow(rowPixels * static_cast(dstChannels) * dstComponentBytes, packParams.Alignment); const SizeT dstOffset = static_cast(std::max(packParams.SkipRows, 0)) * dstRowStride + static_cast(std::max(packParams.SkipPixels, 0)) * - static_cast(dstChannels); + static_cast(dstChannels) * dstComponentBytes; const SizeT packedSize = dstOffset + (static_cast(height - 1) * dstRowStride) + - (static_cast(width) * static_cast(dstChannels)); + (static_cast(width) * static_cast(dstChannels) * dstComponentBytes); Vector packed(packedSize, 0); const Bool srcIsBgra = IsBgraVkFormat(srcFormat); @@ -1729,10 +1773,14 @@ void main() { const Uint8* srcRow = srcPixels + static_cast(row) * static_cast(width) * 4; Uint8* dstRow = packed.data() + dstOffset + static_cast(row) * dstRowStride; for (GLsizei col = 0; col < width; ++col) { - StoreReadbackPixel(srcRow + static_cast(col) * 4, - srcIsBgra, - format, - dstRow + static_cast(col) * static_cast(dstChannels)); + const auto* src = srcRow + static_cast(col) * 4; + auto* dst = dstRow + static_cast(col) * static_cast(dstChannels) * + dstComponentBytes; + if (type == GL_FLOAT) { + StoreReadbackPixelFloat(src, srcIsBgra, format, reinterpret_cast(dst)); + } else { + StoreReadbackPixel(src, srcIsBgra, format, dst); + } } } @@ -4854,9 +4902,9 @@ void main() { void VulkanRenderer::GenerateMipmap(GLenum target) { const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); - const auto uploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); - MOBILEGL_ASSERT(textureTarget == TextureTarget::Texture2D || textureTarget == TextureTarget::Texture3D, - "GenerateMipmap currently only supports GL_TEXTURE_2D and GL_TEXTURE_3D."); + MOBILEGL_ASSERT(textureTarget == TextureTarget::Texture2D || textureTarget == TextureTarget::Texture3D || + textureTarget == TextureTarget::TextureCubeMap, + "GenerateMipmap currently only supports GL_TEXTURE_2D, GL_TEXTURE_3D, and GL_TEXTURE_CUBE_MAP."); auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); auto texture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject(); @@ -4908,8 +4956,7 @@ void main() { "GenerateMipmap: depth-stencil mipmap generation is not supported yet."); } - const Bool allocatedMipmapStorage = - EnsureGenerateMipmapStorageAllocated(*mipmapTexture, uploadTarget, baseMipLevel); + const Bool allocatedMipmapStorage = EnsureGenerateMipmapStorageAllocated(*mipmapTexture, baseMipLevel); MOBILEGL_ASSERT(allocatedMipmapStorage, "GenerateMipmap could not allocate a full mip chain for this texture."); resource = m_textureManager->SyncTextureAndGetDescriptor(*texture); @@ -5021,13 +5068,13 @@ void main() { blitRegion.srcSubresource.aspectMask = resource->aspect; blitRegion.srcSubresource.mipLevel = level - 1; blitRegion.srcSubresource.baseArrayLayer = 0; - blitRegion.srcSubresource.layerCount = 1; + blitRegion.srcSubresource.layerCount = resource->arrayLayers; blitRegion.srcOffsets[0] = {0, 0, 0}; blitRegion.srcOffsets[1] = {srcTexelSize.x(), srcTexelSize.y(), srcTexelSize.z()}; blitRegion.dstSubresource.aspectMask = resource->aspect; blitRegion.dstSubresource.mipLevel = level; blitRegion.dstSubresource.baseArrayLayer = 0; - blitRegion.dstSubresource.layerCount = 1; + blitRegion.dstSubresource.layerCount = resource->arrayLayers; blitRegion.dstOffsets[0] = {0, 0, 0}; blitRegion.dstOffsets[1] = {dstTexelSize.x(), dstTexelSize.y(), dstTexelSize.z()}; diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index c728bcc6..b4e2a454 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -1266,6 +1266,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_Util::ConvertGLEnumToString(target).c_str(), pointIndex, buffer); BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; + if (!BufferImpl::ValidateBufferBindingPointIndex(bufferTarget, pointIndex)) return; auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); SharedPtr bufferObject; @@ -1297,6 +1298,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_Util::ConvertGLEnumToString(target).c_str(), index, buffer, offset, size); BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; + if (!BufferImpl::ValidateBufferBindingPointIndex(bufferTarget, index)) return; auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); SharedPtr bufferObject; diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp index 0946c0da..d03542d3 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "Validators.h" +#include #include #include #include @@ -52,6 +53,26 @@ namespace MobileGL::MG_Impl::GLImpl::BufferImpl { return true; } + Bool ValidateBufferBindingPointIndex(BufferTarget target, Uint index) { + SizeT pointCount = MG_State::pGLContext->GetBufferBindingPointCount(target); + if (target == BufferTarget::ShaderStorage && MG_Backend::pActiveBackendObject) { + const Int backendCount = + MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings; + pointCount = std::min(pointCount, static_cast(std::max(backendCount, 0))); + } + + if (index < pointCount) { + return true; + } + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl/BufferImpl", "ValidateBufferBindingPointIndex", + std::format("Binding point index {} is out of range for target {}.", index, + MG_Util::ConvertBufferTargetToString(target)))); + return false; + } + Bool ValidateBufferName(Uint index, Bool allowZero) { if (index == 0) { if (allowZero) return true; diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/Validators.h b/MobileGL/MG_Impl/GLImpl/Buffer/Validators.h index e98568e5..65ebe5ce 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/Validators.h +++ b/MobileGL/MG_Impl/GLImpl/Buffer/Validators.h @@ -16,4 +16,5 @@ namespace MobileGL::MG_Impl::GLImpl::BufferImpl { Bool ValidateBufferUsage(BufferUsage usage); Bool ValidateBufferMappingAccess(Flags accessBits); Bool ValidateBufferBindingPointTarget(BufferTarget target); + Bool ValidateBufferBindingPointIndex(BufferTarget target, Uint index); } // namespace MobileGL::MG_Impl::GLImpl::BufferImpl diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index e5a0b551..b0d80c1e 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -235,6 +235,25 @@ namespace MobileGL::MG_Impl::GLImpl { return true; } + bool ValidateShaderStorageBlockBinding(GLuint binding) { + SizeT maxBindingCount = MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::ShaderStorage); + if (MG_Backend::pActiveBackendObject) { + const Int backendCount = + MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings; + maxBindingCount = std::min(maxBindingCount, static_cast(std::max(backendCount, 0))); + } + + if (binding < maxBindingCount) { + return true; + } + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, + "Shader storage block binding is out of range.")); + return false; + } + void AttachShader_State(GLuint program, GLuint shader) { auto& programObject = TryToGetProgramObject(program); if (!programObject) return; @@ -1973,6 +1992,7 @@ namespace MobileGL::MG_Impl::GLImpl { void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) { auto& programObject = TryToGetProgramObject(program); if (!programObject || !programObject->GetLinkStatus()) return; + if (!ValidateShaderStorageBlockBinding(storageBlockBinding)) return; auto shaderStorageBlockBinding = MG_Backend::gBackendFunctionsTable.GL.ShaderStorageBlockBinding; if (!shaderStorageBlockBinding) { MG_State::pGLContext->RecordError( diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 01139594..99d536d0 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -3218,13 +3218,21 @@ namespace MobileGL::MG_Impl::GLImpl { void GenerateMipmap(GLenum target) { const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + if (!TextureImpl::ValidateTextureTarget(textureTarget)) { + return; + } auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); auto& textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject(); - if (textureObject) { - auto* mipmapTexture = dynamic_cast(textureObject.get()); - MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage."); - EnsureGeneratedMipmapStorageAllocated(*mipmapTexture); + if (!textureObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "GenerateMipmap requires a bound texture.")); + return; } + + auto* mipmapTexture = dynamic_cast(textureObject.get()); + MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage."); + EnsureGeneratedMipmapStorageAllocated(*mipmapTexture); GenerateMipmap_Backend(target); } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureState.cpp b/MobileGL/MG_State/GLState/TextureState/TextureState.cpp index d58727e1..c40a217f 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureState.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureState.cpp @@ -99,7 +99,7 @@ namespace MobileGL::MG_State::GLState { } for (auto& imageBinding : m_imageTextureBindings) { if (imageBinding.Texture == it->second) { - imageBinding.Bind(nullptr, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); + imageBinding.Bind(nullptr, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8); } } m_textureObjects.erase(it); diff --git a/MobileGL/MG_State/GLState/TextureState/TextureState.h b/MobileGL/MG_State/GLState/TextureState/TextureState.h index e8d85e63..4072e294 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureState.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureState.h @@ -20,7 +20,7 @@ namespace MobileGL::MG_State::GLState { GLboolean Layered = GL_FALSE; GLint Layer = 0; GLenum Access = GL_READ_ONLY; - GLenum Format = GL_RGBA8; + GLenum Format = GL_R8; Uint16 Version = 0; void Bind(SharedPtr texture, GLint level, GLboolean layered, GLint layer, GLenum access,