mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (MG_Backend/DirectVulkan, MG_Backend/DirectGLES): fix vulkan program
cache, get EGLSurfaceSize on viewport = 0
This commit is contained in:
@@ -22,6 +22,9 @@
|
|||||||
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
||||||
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
|
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
|
||||||
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#if defined(__linux__) && !defined(__ANDROID__) && __has_include(<X11/Xlib.h>)
|
#if defined(__linux__) && !defined(__ANDROID__) && __has_include(<X11/Xlib.h>)
|
||||||
#pragma push_macro("Bool")
|
#pragma push_macro("Bool")
|
||||||
#pragma push_macro("None")
|
#pragma push_macro("None")
|
||||||
@@ -35,6 +38,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
MG_External::GLESFunctionsTable g_GLESFuncs;
|
MG_External::GLESFunctionsTable g_GLESFuncs;
|
||||||
MG_External::GLESCapabilities g_GLESCapabilities;
|
MG_External::GLESCapabilities g_GLESCapabilities;
|
||||||
|
|
||||||
|
static Bool QueryCurrentSurfaceSize(Int& outWidth, Int& outHeight);
|
||||||
|
|
||||||
enum class DrawSyncBit : Uint32 {
|
enum class DrawSyncBit : Uint32 {
|
||||||
None = 0,
|
None = 0,
|
||||||
IndexBuffer = 1 << 0,
|
IndexBuffer = 1 << 0,
|
||||||
@@ -375,19 +380,30 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
namespace RenderStateImpl {
|
namespace RenderStateImpl {
|
||||||
static Uint16 g_syncedRenderStateVersion = 0;
|
static Uint16 g_syncedRenderStateVersion = 0;
|
||||||
|
static Bool g_hasSyncedRenderState = false;
|
||||||
static RenderStateParameters g_syncedRenderStateParameters;
|
static RenderStateParameters g_syncedRenderStateParameters;
|
||||||
|
static IntVec4 g_syncedBackendViewport = IntVec4(-1, -1, -1, -1);
|
||||||
void SyncRenderState() {
|
void SyncRenderState() {
|
||||||
#ifdef TRACY_ENABLE
|
#ifdef TRACY_ENABLE
|
||||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||||
#endif
|
#endif
|
||||||
Uint16 currentRenderStateVersion = MG_State::pGLContext->GetRenderStateParametersVersion();
|
Uint16 currentRenderStateVersion = MG_State::pGLContext->GetRenderStateParametersVersion();
|
||||||
if (currentRenderStateVersion == g_syncedRenderStateVersion) return;
|
if (g_hasSyncedRenderState && currentRenderStateVersion == g_syncedRenderStateVersion) return;
|
||||||
|
|
||||||
const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();
|
const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();
|
||||||
|
|
||||||
if (parameters.Viewport != g_syncedRenderStateParameters.Viewport) {
|
IntVec4 backendViewport = parameters.Viewport;
|
||||||
g_GLESFuncs.glViewport(parameters.Viewport.x(), parameters.Viewport.y(), parameters.Viewport.z(),
|
if (backendViewport.z() <= 0 || backendViewport.w() <= 0) {
|
||||||
parameters.Viewport.w());
|
Int surfaceWidth = 0;
|
||||||
|
Int surfaceHeight = 0;
|
||||||
|
if (QueryCurrentSurfaceSize(surfaceWidth, surfaceHeight)) {
|
||||||
|
backendViewport = IntVec4(0, 0, surfaceWidth, surfaceHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (backendViewport != g_syncedBackendViewport) {
|
||||||
|
g_GLESFuncs.glViewport(backendViewport.x(), backendViewport.y(), backendViewport.z(),
|
||||||
|
backendViewport.w());
|
||||||
|
g_syncedBackendViewport = backendViewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SYNC_CAPABILITY(cap_mg, cap_gl) \
|
#define SYNC_CAPABILITY(cap_mg, cap_gl) \
|
||||||
@@ -552,6 +568,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
g_syncedRenderStateVersion = currentRenderStateVersion;
|
g_syncedRenderStateVersion = currentRenderStateVersion;
|
||||||
g_syncedRenderStateParameters = parameters;
|
g_syncedRenderStateParameters = parameters;
|
||||||
|
g_hasSyncedRenderState = true;
|
||||||
}
|
}
|
||||||
} // namespace RenderStateImpl
|
} // namespace RenderStateImpl
|
||||||
|
|
||||||
@@ -1873,6 +1890,73 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
static EGLSurface g_Surface = EGL_NO_SURFACE;
|
static EGLSurface g_Surface = EGL_NO_SURFACE;
|
||||||
static EGLConfig g_Config = nullptr;
|
static EGLConfig g_Config = nullptr;
|
||||||
|
|
||||||
|
static Bool QueryCurrentSurfaceSize(Int& outWidth, Int& outHeight) {
|
||||||
|
outWidth = 0;
|
||||||
|
outHeight = 0;
|
||||||
|
if (!g_EGLFuncs.eglQuerySurface || g_Display == EGL_NO_DISPLAY || g_Surface == EGL_NO_SURFACE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint width = 0;
|
||||||
|
EGLint height = 0;
|
||||||
|
if (!g_EGLFuncs.eglQuerySurface(g_Display, g_Surface, EGL_WIDTH, &width) ||
|
||||||
|
!g_EGLFuncs.eglQuerySurface(g_Display, g_Surface, EGL_HEIGHT, &height) ||
|
||||||
|
width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outWidth = static_cast<Int>(width);
|
||||||
|
outHeight = static_cast<Int>(height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool PresentStatsEnabled() {
|
||||||
|
static const Bool enabled = [] {
|
||||||
|
const char* value = std::getenv("MOBILEGL_GLES_PRESENT_STATS");
|
||||||
|
return value != nullptr && value[0] != '\0' && std::strcmp(value, "0") != 0;
|
||||||
|
}();
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DumpDefaultFramebufferStats() {
|
||||||
|
if (!PresentStatsEnabled() || !g_GLESFuncs.glReadPixels || !g_EGLFuncs.eglQuerySurface ||
|
||||||
|
g_Display == EGL_NO_DISPLAY || g_Surface == EGL_NO_SURFACE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int width = 0;
|
||||||
|
Int height = 0;
|
||||||
|
if (!QueryCurrentSurfaceSize(width, height)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint viewport[4] = {0, 0, 0, 0};
|
||||||
|
g_GLESFuncs.glGetIntegerv(GL_VIEWPORT, viewport);
|
||||||
|
GLint previousReadFramebuffer = 0;
|
||||||
|
g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousReadFramebuffer);
|
||||||
|
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
Vector<Uint8> pixels(static_cast<SizeT>(width) * static_cast<SizeT>(height) * 4);
|
||||||
|
g_GLESFuncs.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
|
||||||
|
SizeT nonBlack = 0;
|
||||||
|
SizeT nonZeroAlpha = 0;
|
||||||
|
for (SizeT offset = 0; offset + 3 < pixels.size(); offset += 4) {
|
||||||
|
if (pixels[offset] != 0 || pixels[offset + 1] != 0 || pixels[offset + 2] != 0) {
|
||||||
|
++nonBlack;
|
||||||
|
}
|
||||||
|
if (pixels[offset + 3] != 0) {
|
||||||
|
++nonZeroAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(previousReadFramebuffer));
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"MOBILEGL_GLES_PRESENT_STATS nonBlack=%zu/%zu alpha=%zu/%zu size=%dx%d viewport=%d,%d,%d,%d\n",
|
||||||
|
nonBlack, pixels.size() / 4, nonZeroAlpha, pixels.size() / 4, width, height,
|
||||||
|
viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(__ANDROID__)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
static void* OpenX11Lib() {
|
static void* OpenX11Lib() {
|
||||||
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
|
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
|
||||||
@@ -2092,6 +2176,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
void Present() {
|
void Present() {
|
||||||
if (g_Display != EGL_NO_DISPLAY && g_Surface != EGL_NO_SURFACE) {
|
if (g_Display != EGL_NO_DISPLAY && g_Surface != EGL_NO_SURFACE) {
|
||||||
|
DumpDefaultFramebufferStats();
|
||||||
|
if (g_GLESFuncs.glFlush) {
|
||||||
|
g_GLESFuncs.glFlush();
|
||||||
|
}
|
||||||
g_EGLFuncs.eglSwapBuffers(g_Display, g_Surface);
|
g_EGLFuncs.eglSwapBuffers(g_Display, g_Surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,17 @@
|
|||||||
namespace MobileGL::MG_Backend::DirectGLES {
|
namespace MobileGL::MG_Backend::DirectGLES {
|
||||||
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true;
|
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true;
|
||||||
|
|
||||||
|
static Uint ResolveBackendEsslVersion() {
|
||||||
|
const auto& version = g_GLESCapabilities.GLESVersion;
|
||||||
|
if (version.Major > 3 || (version.Major == 3 && version.Minor >= 2)) {
|
||||||
|
return 320;
|
||||||
|
}
|
||||||
|
if (version.Major == 3 && version.Minor >= 1) {
|
||||||
|
return 310;
|
||||||
|
}
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
namespace BufferImpl {
|
namespace BufferImpl {
|
||||||
BackendBufferObject::BackendBufferObject() {
|
BackendBufferObject::BackendBufferObject() {
|
||||||
#ifdef TRACY_ENABLE
|
#ifdef TRACY_ENABLE
|
||||||
@@ -1181,8 +1192,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
spvc_compiler_options options;
|
spvc_compiler_options options;
|
||||||
spvcSession.CreateOptions(&options);
|
spvcSession.CreateOptions(&options);
|
||||||
|
|
||||||
// TODO: check ESSL version supported by backend driver
|
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION,
|
||||||
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 320);
|
ResolveBackendEsslVersion());
|
||||||
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE);
|
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE);
|
||||||
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
||||||
|
|
||||||
|
|||||||
@@ -1634,15 +1634,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
const ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram(
|
const ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram(
|
||||||
const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) {
|
const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) {
|
||||||
const Uint32 backendStateVersion = program.GetBackendStateVersion();
|
const HashType hash = ComputeHash(program, flags);
|
||||||
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};
|
|
||||||
}
|
|
||||||
auto it = m_cache.find(hash);
|
auto it = m_cache.find(hash);
|
||||||
if (it != m_cache.end()) {
|
if (it != m_cache.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
||||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
@@ -74,6 +77,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return uniformUnit >= 0 ? uniformUnit : 0;
|
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,
|
Bool UniformManager::Initialize(VkDevice device, VkBufferManager* bufferManager,
|
||||||
ProgramFactory* programFactory,
|
ProgramFactory* programFactory,
|
||||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||||
@@ -262,6 +273,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.imageView = resource->fullView,
|
.imageView = resource->fullView,
|
||||||
.imageLayout = resource->layout,
|
.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;
|
return outImageInfo.sampler != VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
|
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
|
||||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
@@ -316,6 +319,99 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return true;
|
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) {
|
static VkComponentSwizzle ToVkComponentSwizzle(TextureSwizzleParam swizzle) {
|
||||||
switch (swizzle) {
|
switch (swizzle) {
|
||||||
case TextureSwizzleParam::Red:
|
case TextureSwizzleParam::Red:
|
||||||
@@ -777,6 +873,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DumpTextureSyncStats(texture.GetExternalIndex(), texture.GetFormat(), uploadTarget, mipLevelCount,
|
||||||
|
texelSize, byteSize, hasDirtyMipLevel);
|
||||||
if (!hasDirtyMipLevel) {
|
if (!hasDirtyMipLevel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1001,6 +1099,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
DeferViewRelease(resource.fullView);
|
DeferViewRelease(resource.fullView);
|
||||||
resource.fullView = VK_NULL_HANDLE;
|
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 TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat());
|
||||||
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo);
|
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo);
|
||||||
@@ -1111,6 +1215,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (!uploadItems.back().expandedData.empty()) {
|
if (!uploadItems.back().expandedData.empty()) {
|
||||||
uploadItems.back().source = uploadItems.back().expandedData.data();
|
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);
|
stagingSize += static_cast<VkDeviceSize>(uploadItems.back().uploadByteSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1156,14 +1264,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
|
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
|
||||||
|
|
||||||
const VkImageAspectFlags aspectMask = GetAspectMaskForFormat(outResource.format);
|
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,
|
Bool ok = TransitionImageLayout(commandBuffer, outResource.image,
|
||||||
outResource.layout,
|
outResource.layout,
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ?
|
uploadSrcStageMask,
|
||||||
kGraphicsSampledReadStages :
|
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_ACCESS_SHADER_READ_BIT : 0,
|
uploadSrcAccessMask,
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
aspectMask, 0, outResource.mipLevels, outResource.arrayLayers);
|
aspectMask, 0, outResource.mipLevels, outResource.arrayLayers);
|
||||||
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
|
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
|
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
|
||||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
@@ -376,6 +379,94 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return attributeMask;
|
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) {
|
static Bool TryGetCurrentVertexAttributeFormat(GLenum glType, VkFormat& outFormat) {
|
||||||
switch (glType) {
|
switch (glType) {
|
||||||
case GL_FLOAT:
|
case GL_FLOAT:
|
||||||
@@ -1398,12 +1489,17 @@ void main() {
|
|||||||
if (binding >= vertexInputState.bindings.size()) {
|
if (binding >= vertexInputState.bindings.size()) {
|
||||||
break;
|
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() &&
|
const Bool usesClientMemory = binding < vertexInputState.bindingUsesClientMemory.size() &&
|
||||||
vertexInputState.bindingUsesClientMemory[binding];
|
vertexInputState.bindingUsesClientMemory[binding];
|
||||||
if (usesClientMemory) {
|
if (usesClientMemory) {
|
||||||
const Uint32 location = binding < vertexInputState.bindingAttributeLocations.size()
|
const Uint32 location = bindingLocation;
|
||||||
? vertexInputState.bindingAttributeLocations[binding]
|
|
||||||
: static_cast<Uint32>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS);
|
|
||||||
MOBILEGL_ASSERT(location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS,
|
MOBILEGL_ASSERT(location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS,
|
||||||
"UploadAndBindVertexStreams failed to resolve client attribute location");
|
"UploadAndBindVertexStreams failed to resolve client attribute location");
|
||||||
|
|
||||||
@@ -3840,7 +3936,8 @@ void main() {
|
|||||||
VkBufferObject presentStatsReadback;
|
VkBufferObject presentStatsReadback;
|
||||||
VkDeviceSize presentStatsReadbackSize = 0;
|
VkDeviceSize presentStatsReadbackSize = 0;
|
||||||
const VkExtent2D presentStatsExtent = m_swapchainObject.GetExtent();
|
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;
|
presentStatsExtent.width > 0 && presentStatsExtent.height > 0;
|
||||||
if (collectPresentStats) {
|
if (collectPresentStats) {
|
||||||
presentStatsReadbackSize = static_cast<VkDeviceSize>(presentStatsExtent.width) *
|
presentStatsReadbackSize = static_cast<VkDeviceSize>(presentStatsExtent.width) *
|
||||||
@@ -3909,6 +4006,7 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(pixels != nullptr, "Present stats: failed to map readback buffer");
|
MOBILEGL_ASSERT(pixels != nullptr, "Present stats: failed to map readback buffer");
|
||||||
SizeT nonBlack = 0;
|
SizeT nonBlack = 0;
|
||||||
SizeT nonTransparent = 0;
|
SizeT nonTransparent = 0;
|
||||||
|
SizeT colored = 0;
|
||||||
const SizeT pixelCount = static_cast<SizeT>(presentStatsExtent.width) *
|
const SizeT pixelCount = static_cast<SizeT>(presentStatsExtent.width) *
|
||||||
static_cast<SizeT>(presentStatsExtent.height);
|
static_cast<SizeT>(presentStatsExtent.height);
|
||||||
for (SizeT i = 0; i < pixelCount; ++i) {
|
for (SizeT i = 0; i < pixelCount; ++i) {
|
||||||
@@ -3916,15 +4014,34 @@ void main() {
|
|||||||
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
|
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
|
||||||
++nonBlack;
|
++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) {
|
if (p[3] != 0) {
|
||||||
++nonTransparent;
|
++nonTransparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (PresentStatsEnabled()) {
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"MOBILEGL_PRESENT_STATS nonBlack=%zu/%zu alpha=%zu/%zu size=%ux%u\n",
|
"MOBILEGL_PRESENT_STATS nonBlack=%zu/%zu colored=%zu/%zu alpha=%zu/%zu size=%ux%u\n",
|
||||||
nonBlack, pixelCount, nonTransparent, pixelCount,
|
nonBlack, pixelCount, colored, pixelCount, nonTransparent, pixelCount,
|
||||||
presentStatsExtent.width, presentStatsExtent.height);
|
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.isCommandRecording = false;
|
||||||
frame.hasCommandBufferRecorded = false;
|
frame.hasCommandBufferRecorded = false;
|
||||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||||
|
|||||||
Reference in New Issue
Block a user