[Fix] (MG_Backend/DirectVulkan, MG_Backend/DirectGLES): fix vulkan program

cache, get EGLSurfaceSize on viewport = 0
This commit is contained in:
2026-06-08 05:41:38 +08:00
parent d1a5a4e39c
commit 357807666d
6 changed files with 375 additions and 32 deletions
@@ -1634,15 +1634,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram(
const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) {
const Uint32 backendStateVersion = program.GetBackendStateVersion();
HashType hash = 0;
if (m_lastLookup.program == &program && m_lastLookup.backendStateVersion == backendStateVersion &&
m_lastLookup.flags == flags) {
hash = m_lastLookup.hash;
} else {
hash = ComputeHash(program, flags);
m_lastLookup = {.program = &program, .backendStateVersion = backendStateVersion, .flags = flags, .hash = hash};
}
const HashType hash = ComputeHash(program, flags);
auto it = m_cache.find(hash);
if (it != m_cache.end()) {
return it->second;
@@ -16,6 +16,9 @@
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include "MG_Util/Metrics/TextureMetrics.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
namespace MobileGL::MG_Backend::DirectVulkan {
@@ -74,6 +77,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return uniformUnit >= 0 ? uniformUnit : 0;
}
static Bool ShouldDumpDescriptorStats() {
static const Bool enabled = [] {
const char* value = std::getenv("MOBILEGL_DESCRIPTOR_STATS");
return value != nullptr && value[0] != '\0' && std::strcmp(value, "0") != 0;
}();
return enabled;
}
Bool UniformManager::Initialize(VkDevice device, VkBufferManager* bufferManager,
ProgramFactory* programFactory,
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
@@ -262,6 +273,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.imageView = resource->fullView,
.imageLayout = resource->layout,
};
if (ShouldDumpDescriptorStats()) {
std::fprintf(stderr,
"MOBILEGL_DESCRIPTOR_STATS program=%u binding=%u name=%s location=%d unit=%d "
"texture=%u target=%d sampler=%p imageView=%p layout=%d\n",
program.GetExternalIndex(),
binding,
programObj.samplerNameByBinding[binding].c_str(),
location,
unit,
texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()),
reinterpret_cast<void*>(outImageInfo.sampler),
reinterpret_cast<void*>(outImageInfo.imageView),
static_cast<Int>(outImageInfo.imageLayout));
}
return outImageInfo.sampler != VK_NULL_HANDLE;
}
@@ -12,6 +12,9 @@
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
namespace MobileGL::MG_Backend::DirectVulkan {
@@ -316,6 +319,99 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
static Bool ShouldDumpTextureUploadStats() {
static const Bool enabled = [] {
const char* value = std::getenv("MOBILEGL_TEXTURE_UPLOAD_STATS");
return value != nullptr && value[0] != '\0' && std::strcmp(value, "0") != 0;
}();
return enabled;
}
static void DumpTextureSyncStats(Int textureId, TextureInternalFormat format, TextureUploadTarget uploadTarget,
Uint32 mipLevelCount, const IntVec3& texelSize, SizeT byteSize,
Bool hasDirtyMipLevel) {
if (!ShouldDumpTextureUploadStats()) {
return;
}
std::fprintf(stderr,
"MOBILEGL_TEXTURE_SYNC_STATS texture=%d format=%s target=%s mips=%u size=%dx%dx%d "
"bytes=%zu dirty=%d\n",
textureId,
MG_Util::ConvertTextureInternalFormatToString(format).c_str(),
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
mipLevelCount,
texelSize.x(),
texelSize.y(),
texelSize.z(),
byteSize,
hasDirtyMipLevel ? 1 : 0);
}
static void DumpTextureUploadStats(Int textureId, TextureUploadTarget target, Uint32 level,
const IntVec3& texelSize, const void* data, SizeT byteSize, Uint32 channels) {
if (!ShouldDumpTextureUploadStats() || data == nullptr || byteSize == 0 || channels == 0) {
return;
}
const auto* bytes = static_cast<const Uint8*>(data);
Uint8 minValue = 255;
Uint8 maxValue = 0;
SizeT nonZero = 0;
Uint64 sum = 0;
Uint64 neighborDiff = 0;
SizeT neighborCount = 0;
for (SizeT i = 0; i < byteSize; ++i) {
minValue = std::min(minValue, bytes[i]);
maxValue = std::max(maxValue, bytes[i]);
nonZero += bytes[i] != 0 ? 1 : 0;
sum += bytes[i];
}
const SizeT width = static_cast<SizeT>(std::max(texelSize.x(), 0));
const SizeT height = static_cast<SizeT>(std::max(texelSize.y(), 0));
const SizeT pixelStride = channels;
if (width > 1 && height > 0 && byteSize >= width * height * pixelStride) {
for (SizeT y = 0; y < height; ++y) {
const SizeT row = y * width * pixelStride;
for (SizeT x = 1; x < width; ++x) {
const SizeT prev = row + (x - 1) * pixelStride;
const SizeT cur = row + x * pixelStride;
for (SizeT c = 0; c < std::min<SizeT>(channels, 3); ++c) {
neighborDiff += static_cast<Uint64>(
std::abs(static_cast<Int>(bytes[cur + c]) - static_cast<Int>(bytes[prev + c])));
++neighborCount;
}
}
}
}
const double avg = byteSize > 0 ? static_cast<double>(sum) / static_cast<double>(byteSize) : 0.0;
const double avgNeighborDiff =
neighborCount > 0 ? static_cast<double>(neighborDiff) / static_cast<double>(neighborCount) : 0.0;
std::fprintf(stderr,
"MOBILEGL_TEXTURE_UPLOAD_STATS texture=%d target=%s level=%u size=%dx%dx%d bytes=%zu "
"channels=%u min=%u max=%u avg=%.2f nonzero=%zu neighborDiff=%.2f first=%u,%u,%u,%u\n",
textureId,
MG_Util::ConvertTextureUploadTargetToString(target).c_str(),
level,
texelSize.x(),
texelSize.y(),
texelSize.z(),
byteSize,
channels,
static_cast<Uint>(minValue),
static_cast<Uint>(maxValue),
avg,
nonZero,
avgNeighborDiff,
byteSize > 0 ? static_cast<Uint>(bytes[0]) : 0u,
byteSize > 1 ? static_cast<Uint>(bytes[1]) : 0u,
byteSize > 2 ? static_cast<Uint>(bytes[2]) : 0u,
byteSize > 3 ? static_cast<Uint>(bytes[3]) : 0u);
}
static VkComponentSwizzle ToVkComponentSwizzle(TextureSwizzleParam swizzle) {
switch (swizzle) {
case TextureSwizzleParam::Red:
@@ -777,6 +873,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
break;
}
}
DumpTextureSyncStats(texture.GetExternalIndex(), texture.GetFormat(), uploadTarget, mipLevelCount,
texelSize, byteSize, hasDirtyMipLevel);
if (!hasDirtyMipLevel) {
return true;
}
@@ -1001,6 +1099,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
DeferViewRelease(resource.fullView);
resource.fullView = VK_NULL_HANDLE;
}
for (auto& sampledView : resource.perMipSampledViews) {
if (sampledView != VK_NULL_HANDLE) {
DeferViewRelease(sampledView);
sampledView = VK_NULL_HANDLE;
}
}
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat());
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo);
@@ -1111,6 +1215,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!uploadItems.back().expandedData.empty()) {
uploadItems.back().source = uploadItems.back().expandedData.data();
}
DumpTextureUploadStats(mipmapTexture.GetExternalIndex(), uploadItems.back().target,
uploadItems.back().level, uploadItems.back().texelSize,
uploadItems.back().source, uploadItems.back().uploadByteSize,
formatInfo.expandRgbToRgba ? 4u : 0u);
stagingSize += static_cast<VkDeviceSize>(uploadItems.back().uploadByteSize);
}
}
@@ -1156,16 +1264,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
const VkImageAspectFlags aspectMask = GetAspectMaskForFormat(outResource.format);
VkPipelineStageFlags uploadSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags uploadSrcAccessMask = 0;
GetImageTransitionSourceState(outResource.layout, uploadSrcStageMask, uploadSrcAccessMask);
Bool ok = TransitionImageLayout(commandBuffer, outResource.image,
outResource.layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ?
kGraphicsSampledReadStages :
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_ACCESS_SHADER_READ_BIT : 0,
VK_ACCESS_TRANSFER_WRITE_BIT,
aspectMask, 0, outResource.mipLevels, outResource.arrayLayers);
outResource.layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
uploadSrcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT,
uploadSrcAccessMask,
VK_ACCESS_TRANSFER_WRITE_BIT,
aspectMask, 0, outResource.mipLevels, outResource.arrayLayers);
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
for (const auto& item : uploadItems) {
@@ -20,6 +20,9 @@
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include "MG_Util/Metrics/TextureMetrics.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vulkan/vulkan_core.h>
namespace MobileGL::MG_Backend::DirectVulkan {
@@ -376,6 +379,94 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return attributeMask;
}
static Bool ShouldDumpVertexInputStats() {
static const Bool enabled = [] {
const char* value = std::getenv("MOBILEGL_VERTEX_INPUT_STATS");
return value != nullptr && value[0] != '\0' && std::strcmp(value, "0") != 0;
}();
return enabled;
}
static const char* PresentDumpPath() {
const char* value = std::getenv("MOBILEGL_PRESENT_DUMP_PATH");
return value != nullptr && value[0] != '\0' ? value : nullptr;
}
static void DumpVertexInputStats(Uint32 location, const MG_State::GLState::VertexAttribute& attr,
Uint32 firstVertex, Uint32 vertexCount) {
if (!ShouldDumpVertexInputStats() || attr.Type != DataType::Float32 || attr.Size <= 0 || attr.Size > 4) {
return;
}
const SizeT componentSize = sizeof(float);
const SizeT elementSize = componentSize * static_cast<SizeT>(attr.Size);
const SizeT stride = attr.Stride > 0 ? static_cast<SizeT>(attr.Stride) : elementSize;
const Uint8* base = nullptr;
SizeT available = 0;
if (attr.Buffer) {
const auto& data = attr.Buffer->GetDataReadOnly();
if (!data || attr.Offset >= data->size()) {
return;
}
base = data->data() + attr.Offset;
available = data->size() - attr.Offset;
} else {
base = reinterpret_cast<const Uint8*>(attr.Offset);
available = static_cast<SizeT>(firstVertex + vertexCount) * stride;
}
if (base == nullptr || vertexCount == 0 || available < elementSize) {
return;
}
float minValues[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float maxValues[4] = {0.0f, 0.0f, 0.0f, 0.0f};
Bool initialized = false;
SizeT sampled = 0;
const Uint32 maxSamples = std::min<Uint32>(vertexCount, 256);
for (Uint32 sample = 0; sample < maxSamples; ++sample) {
const SizeT offset = static_cast<SizeT>(firstVertex + sample) * stride;
if (offset + elementSize > available) {
break;
}
const auto* values = reinterpret_cast<const float*>(base + offset);
for (Int component = 0; component < attr.Size; ++component) {
if (!initialized) {
minValues[component] = values[component];
maxValues[component] = values[component];
} else {
minValues[component] = std::min(minValues[component], values[component]);
maxValues[component] = std::max(maxValues[component], values[component]);
}
}
initialized = true;
++sampled;
}
if (!initialized) {
return;
}
std::fprintf(stderr,
"MOBILEGL_VERTEX_INPUT_STATS loc=%u buffer=%u size=%d stride=%zu first=%u count=%u sampled=%zu "
"min=(%.3f,%.3f,%.3f,%.3f) max=(%.3f,%.3f,%.3f,%.3f)\n",
location,
attr.Buffer ? attr.Buffer->GetExternalIndex() : 0u,
attr.Size,
stride,
firstVertex,
vertexCount,
sampled,
minValues[0],
minValues[1],
minValues[2],
minValues[3],
maxValues[0],
maxValues[1],
maxValues[2],
maxValues[3]);
}
static Bool TryGetCurrentVertexAttributeFormat(GLenum glType, VkFormat& outFormat) {
switch (glType) {
case GL_FLOAT:
@@ -1398,12 +1489,17 @@ void main() {
if (binding >= vertexInputState.bindings.size()) {
break;
}
const Uint32 bindingLocation = binding < vertexInputState.bindingAttributeLocations.size()
? vertexInputState.bindingAttributeLocations[binding]
: static_cast<Uint32>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS);
if (bindingLocation < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS) {
DumpVertexInputStats(bindingLocation, vao.GetAttribute(bindingLocation),
drawParams.firstVertex, drawParams.vertexCount);
}
const Bool usesClientMemory = binding < vertexInputState.bindingUsesClientMemory.size() &&
vertexInputState.bindingUsesClientMemory[binding];
if (usesClientMemory) {
const Uint32 location = binding < vertexInputState.bindingAttributeLocations.size()
? vertexInputState.bindingAttributeLocations[binding]
: static_cast<Uint32>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS);
const Uint32 location = bindingLocation;
MOBILEGL_ASSERT(location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS,
"UploadAndBindVertexStreams failed to resolve client attribute location");
@@ -3840,7 +3936,8 @@ void main() {
VkBufferObject presentStatsReadback;
VkDeviceSize presentStatsReadbackSize = 0;
const VkExtent2D presentStatsExtent = m_swapchainObject.GetExtent();
const Bool collectPresentStats = PresentStatsEnabled() && frame.isCommandRecording &&
const char* presentDumpPath = PresentDumpPath();
const Bool collectPresentStats = (PresentStatsEnabled() || presentDumpPath != nullptr) && frame.isCommandRecording &&
presentStatsExtent.width > 0 && presentStatsExtent.height > 0;
if (collectPresentStats) {
presentStatsReadbackSize = static_cast<VkDeviceSize>(presentStatsExtent.width) *
@@ -3909,6 +4006,7 @@ void main() {
MOBILEGL_ASSERT(pixels != nullptr, "Present stats: failed to map readback buffer");
SizeT nonBlack = 0;
SizeT nonTransparent = 0;
SizeT colored = 0;
const SizeT pixelCount = static_cast<SizeT>(presentStatsExtent.width) *
static_cast<SizeT>(presentStatsExtent.height);
for (SizeT i = 0; i < pixelCount; ++i) {
@@ -3916,14 +4014,33 @@ void main() {
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
++nonBlack;
}
const Uint8 minRgb = std::min(p[0], std::min(p[1], p[2]));
const Uint8 maxRgb = std::max(p[0], std::max(p[1], p[2]));
if (maxRgb - minRgb > 24) {
++colored;
}
if (p[3] != 0) {
++nonTransparent;
}
}
std::fprintf(stderr,
"MOBILEGL_PRESENT_STATS nonBlack=%zu/%zu alpha=%zu/%zu size=%ux%u\n",
nonBlack, pixelCount, nonTransparent, pixelCount,
presentStatsExtent.width, presentStatsExtent.height);
if (PresentStatsEnabled()) {
std::fprintf(stderr,
"MOBILEGL_PRESENT_STATS nonBlack=%zu/%zu colored=%zu/%zu alpha=%zu/%zu size=%ux%u\n",
nonBlack, pixelCount, colored, pixelCount, nonTransparent, pixelCount,
presentStatsExtent.width, presentStatsExtent.height);
}
if (presentDumpPath != nullptr) {
FILE* dump = std::fopen(presentDumpPath, "wb");
if (dump != nullptr) {
std::fprintf(dump, "P6\n%u %u\n255\n", presentStatsExtent.width, presentStatsExtent.height);
for (SizeT i = 0; i < pixelCount; ++i) {
const Uint8* p = pixels + i * 4;
const Uint8 rgb[3] = {p[0], p[1], p[2]};
std::fwrite(rgb, 1, sizeof(rgb), dump);
}
std::fclose(dump);
}
}
}
frame.isCommandRecording = false;
frame.hasCommandBufferRecorded = false;