mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Fix] (MG_Backend/DirectVulkan): fix Minecraft 26.2 startup crashes
- Support cube map face uploads with cube-compatible images and per-face array layers - Add uniform texel buffer descriptor support for samplerBuffer bindings - Cache transient vertex/index buffer uploads per frame to avoid VMA allocation failures
This commit is contained in:
@@ -43,6 +43,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 arrayLayers = 1;
|
||||
};
|
||||
|
||||
static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) {
|
||||
return target >= TextureUploadTarget::CubeMapPositiveX &&
|
||||
target <= TextureUploadTarget::CubeMapNegativeZ;
|
||||
}
|
||||
|
||||
static Uint32 ResolveUploadArrayLayer(TextureUploadTarget target) {
|
||||
if (!IsCubeMapFaceUploadTarget(target)) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<Uint32>(target) - static_cast<Uint32>(TextureUploadTarget::CubeMapPositiveX);
|
||||
}
|
||||
|
||||
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
||||
switch (layout) {
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
@@ -174,7 +186,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
commandBuffer, newResource.image, newResource.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT, newResource.aspect, 0, newResource.mipLevels);
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT, newResource.aspect, 0, newResource.mipLevels,
|
||||
newResource.arrayLayers);
|
||||
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to prepare destination image");
|
||||
|
||||
VkImageLayout srcTrackedLayout = oldResource.layout;
|
||||
@@ -184,7 +197,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
ok = VkTextureManager::TransitionImageLayout(
|
||||
commandBuffer, oldResource.image, srcTrackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, oldResource.aspect, 0, preservedMipLevels);
|
||||
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, oldResource.aspect, 0, preservedMipLevels,
|
||||
oldResource.arrayLayers);
|
||||
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to prepare source image");
|
||||
|
||||
Vector<VkImageCopy> copyRegions;
|
||||
@@ -216,7 +230,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
ok = VkTextureManager::TransitionImageLayout(
|
||||
commandBuffer, newResource.image, newResource.layout, oldResource.layout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, newResource.aspect, 0, newResource.mipLevels);
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, newResource.aspect, 0, newResource.mipLevels,
|
||||
newResource.arrayLayers);
|
||||
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to restore destination layout");
|
||||
|
||||
VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture preserve)");
|
||||
@@ -351,6 +366,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
outShape.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
||||
outShape.depth = static_cast<Uint32>(texelSize.z());
|
||||
return true;
|
||||
case TextureUploadTarget::CubeMapPositiveX:
|
||||
case TextureUploadTarget::CubeMapNegativeX:
|
||||
case TextureUploadTarget::CubeMapPositiveY:
|
||||
case TextureUploadTarget::CubeMapNegativeY:
|
||||
case TextureUploadTarget::CubeMapPositiveZ:
|
||||
case TextureUploadTarget::CubeMapNegativeZ:
|
||||
case TextureUploadTarget::ProxyCubeMap:
|
||||
MOBILEGL_ASSERT(texture.GetTarget() == TextureTarget::TextureCubeMap,
|
||||
"TryResolveTextureShapeInfo: cube upload target on non-cube textureId=%d target=%s",
|
||||
texture.GetExternalIndex(),
|
||||
MG_Util::ConvertTextureTargetToString(texture.GetTarget()).c_str());
|
||||
MOBILEGL_ASSERT(texelSize.x() == texelSize.y(),
|
||||
"TryResolveTextureShapeInfo: cube map textureId=%d is not square (%d x %d)",
|
||||
texture.GetExternalIndex(), texelSize.x(), texelSize.y());
|
||||
outShape.imageType = VK_IMAGE_TYPE_2D;
|
||||
outShape.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
outShape.imageFlags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
outShape.depth = 1;
|
||||
outShape.arrayLayers = 6;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -518,7 +553,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const Bool lowerTransitioned = TransitionImageLayout(
|
||||
commandBuffer, resource.image, lowerMipLayout, newLayout,
|
||||
srcStageMask, dstStageMask, srcAccessMask, dstAccessMask,
|
||||
resource.aspect, 0, writtenMipLevel);
|
||||
resource.aspect, 0, writtenMipLevel, resource.arrayLayers);
|
||||
MOBILEGL_ASSERT(lowerTransitioned,
|
||||
"UpdateTrackedImageLayoutAfterAttachmentWrite: failed to transition lower mip levels for textureId=%d",
|
||||
texture->GetExternalIndex());
|
||||
@@ -530,7 +565,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const Bool upperTransitioned = TransitionImageLayout(
|
||||
commandBuffer, resource.image, upperMipLayout, newLayout,
|
||||
srcStageMask, dstStageMask, srcAccessMask, dstAccessMask,
|
||||
resource.aspect, upperBaseMipLevel, resource.mipLevels - upperBaseMipLevel);
|
||||
resource.aspect, upperBaseMipLevel, resource.mipLevels - upperBaseMipLevel,
|
||||
resource.arrayLayers);
|
||||
MOBILEGL_ASSERT(upperTransitioned,
|
||||
"UpdateTrackedImageLayoutAfterAttachmentWrite: failed to transition upper mip levels for textureId=%d",
|
||||
texture->GetExternalIndex());
|
||||
@@ -583,7 +619,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask,
|
||||
kGraphicsSampledReadStages, srcAccessMask,
|
||||
VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels);
|
||||
VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels,
|
||||
resource->arrayLayers);
|
||||
MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex());
|
||||
return ok;
|
||||
}
|
||||
@@ -592,7 +629,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkImageLayout& trackedLayout, VkImageLayout newLayout,
|
||||
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
|
||||
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
|
||||
VkImageAspectFlags aspectMask, Uint32 baseMipLevel, Uint32 levelCount) {
|
||||
VkImageAspectFlags aspectMask, Uint32 baseMipLevel, Uint32 levelCount,
|
||||
Uint32 layerCount) {
|
||||
MOBILEGL_ASSERT(image != VK_NULL_HANDLE, "TransitionImageLayout: m_image == VK_NULL_HANDLE");
|
||||
MOBILEGL_ASSERT(!((dstAccessMask & VK_ACCESS_TRANSFER_READ_BIT) != 0 &&
|
||||
(dstStageMask & VK_PIPELINE_STAGE_TRANSFER_BIT) == 0),
|
||||
@@ -617,7 +655,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
barrier.subresourceRange.baseMipLevel = baseMipLevel;
|
||||
barrier.subresourceRange.levelCount = levelCount;
|
||||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
barrier.subresourceRange.layerCount = layerCount;
|
||||
vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
trackedLayout = newLayout;
|
||||
@@ -666,10 +704,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<TextureUploadTarget> dirtyTargets;
|
||||
if (outResource.viewType == VK_IMAGE_VIEW_TYPE_CUBE) {
|
||||
dirtyTargets = mipTexture->GetUploadTargets();
|
||||
} else {
|
||||
dirtyTargets.push_back(uploadTarget);
|
||||
}
|
||||
Bool hasDirtyMipLevel = false;
|
||||
for (Uint32 level = 0; level < mipLevelCount; ++level) {
|
||||
if (mipTexture->IsStorageDirty(uploadTarget, level)) {
|
||||
hasDirtyMipLevel = true;
|
||||
for (const TextureUploadTarget target : dirtyTargets) {
|
||||
const Uint32 targetMipLevelCount = std::min(mipLevelCount, GetUploadMipLevelCount(*mipTexture, target));
|
||||
for (Uint32 level = 0; level < targetMipLevelCount; ++level) {
|
||||
if (mipTexture->IsStorageDirty(target, level)) {
|
||||
hasDirtyMipLevel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDirtyMipLevel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -933,7 +983,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
TextureUploadTarget uploadTarget,
|
||||
TextureResource &outResource) {
|
||||
struct UploadItem {
|
||||
TextureUploadTarget target = TextureUploadTarget::Unknown;
|
||||
Uint32 level = 0;
|
||||
Uint32 baseArrayLayer = 0;
|
||||
SizeT uploadByteSize = 0;
|
||||
IntVec3 texelSize = {0, 0, 0};
|
||||
const void* source = nullptr;
|
||||
@@ -942,52 +994,64 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
};
|
||||
|
||||
Vector<UploadItem> uploadItems;
|
||||
const Uint32 definedMipLevels = GetUploadMipLevelCount(mipmapTexture, uploadTarget);
|
||||
MOBILEGL_ASSERT(definedMipLevels <= outResource.mipLevels,
|
||||
"UploadDirtyMipLevels: defined mip level count %u exceeds backing mip level count %u for textureId=%d",
|
||||
definedMipLevels, outResource.mipLevels, mipmapTexture.GetExternalIndex());
|
||||
|
||||
uploadItems.reserve(definedMipLevels);
|
||||
Vector<TextureUploadTarget> targets;
|
||||
if (outResource.viewType == VK_IMAGE_VIEW_TYPE_CUBE) {
|
||||
targets = mipmapTexture.GetUploadTargets();
|
||||
} else {
|
||||
targets.push_back(uploadTarget);
|
||||
}
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(mipmapTexture.GetFormat());
|
||||
|
||||
VkDeviceSize stagingSize = 0;
|
||||
for (Uint32 level = 0; level < definedMipLevels; ++level) {
|
||||
if (!mipmapTexture.IsStorageDirty(uploadTarget, level)) {
|
||||
continue;
|
||||
}
|
||||
for (const TextureUploadTarget target : targets) {
|
||||
const Uint32 definedMipLevels = GetUploadMipLevelCount(mipmapTexture, target);
|
||||
MOBILEGL_ASSERT(definedMipLevels <= outResource.mipLevels,
|
||||
"UploadDirtyMipLevels: defined mip level count %u exceeds backing mip level count %u for textureId=%d target=%s",
|
||||
definedMipLevels, outResource.mipLevels, mipmapTexture.GetExternalIndex(),
|
||||
MG_Util::ConvertTextureUploadTargetToString(target).c_str());
|
||||
|
||||
const auto texelSize = mipmapTexture.GetMipmapTexelSize(uploadTarget, level);
|
||||
const auto byteSize = mipmapTexture.GetMipmapByteSize(uploadTarget, level);
|
||||
if (texelSize.x() <= 0 || texelSize.y() <= 0 || byteSize == 0) {
|
||||
mipmapTexture.MarkStorageDirty(uploadTarget, level, false);
|
||||
continue;
|
||||
}
|
||||
for (Uint32 level = 0; level < definedMipLevels; ++level) {
|
||||
if (!mipmapTexture.IsStorageDirty(target, level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const void* source = mipmapTexture.MapMipmapData(uploadTarget, level);
|
||||
if (source == nullptr) {
|
||||
MGLOG_D("%s: MapmipmapData failed at level %d", __func__, level);
|
||||
return false;
|
||||
}
|
||||
const auto texelSize = mipmapTexture.GetMipmapTexelSize(target, level);
|
||||
const auto byteSize = mipmapTexture.GetMipmapByteSize(target, level);
|
||||
if (texelSize.x() <= 0 || texelSize.y() <= 0 || byteSize == 0) {
|
||||
mipmapTexture.MarkStorageDirty(target, level, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
UploadItem uploadItem{};
|
||||
uploadItem.level = level;
|
||||
uploadItem.texelSize = texelSize;
|
||||
uploadItem.source = source;
|
||||
uploadItem.offset = stagingSize;
|
||||
uploadItem.uploadByteSize = byteSize;
|
||||
if (formatInfo.expandRgbToRgba) {
|
||||
const Bool expanded = ExpandRgbSourceToRgba(source, byteSize, texelSize, formatInfo,
|
||||
uploadItem.expandedData);
|
||||
MOBILEGL_ASSERT(expanded,
|
||||
"UploadDirtyMipLevels: failed to expand RGB textureId=%d level=%u to RGBA staging data",
|
||||
mipmapTexture.GetExternalIndex(), level);
|
||||
uploadItem.uploadByteSize = uploadItem.expandedData.size();
|
||||
const void* source = mipmapTexture.MapMipmapData(target, level);
|
||||
if (source == nullptr) {
|
||||
MGLOG_D("%s: MapmipmapData failed at target %s level %d", __func__,
|
||||
MG_Util::ConvertTextureUploadTargetToString(target).c_str(), level);
|
||||
return false;
|
||||
}
|
||||
|
||||
UploadItem uploadItem{};
|
||||
uploadItem.target = target;
|
||||
uploadItem.level = level;
|
||||
uploadItem.baseArrayLayer = ResolveUploadArrayLayer(target);
|
||||
uploadItem.texelSize = texelSize;
|
||||
uploadItem.source = source;
|
||||
uploadItem.offset = stagingSize;
|
||||
uploadItem.uploadByteSize = byteSize;
|
||||
if (formatInfo.expandRgbToRgba) {
|
||||
const Bool expanded = ExpandRgbSourceToRgba(source, byteSize, texelSize, formatInfo,
|
||||
uploadItem.expandedData);
|
||||
MOBILEGL_ASSERT(expanded,
|
||||
"UploadDirtyMipLevels: failed to expand RGB textureId=%d target=%s level=%u to RGBA staging data",
|
||||
mipmapTexture.GetExternalIndex(),
|
||||
MG_Util::ConvertTextureUploadTargetToString(target).c_str(), level);
|
||||
uploadItem.uploadByteSize = uploadItem.expandedData.size();
|
||||
}
|
||||
uploadItems.push_back(Move(uploadItem));
|
||||
if (!uploadItems.back().expandedData.empty()) {
|
||||
uploadItems.back().source = uploadItems.back().expandedData.data();
|
||||
}
|
||||
stagingSize += static_cast<VkDeviceSize>(uploadItems.back().uploadByteSize);
|
||||
}
|
||||
uploadItems.push_back(Move(uploadItem));
|
||||
if (!uploadItems.back().expandedData.empty()) {
|
||||
uploadItems.back().source = uploadItems.back().expandedData.data();
|
||||
}
|
||||
stagingSize += static_cast<VkDeviceSize>(uploadItems.back().uploadByteSize);
|
||||
}
|
||||
|
||||
if (uploadItems.empty()) {
|
||||
@@ -1034,13 +1098,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
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.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);
|
||||
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
|
||||
|
||||
for (const auto& item : uploadItems) {
|
||||
@@ -1050,7 +1114,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
copy.bufferImageHeight = 0;
|
||||
copy.imageSubresource.aspectMask = aspectMask;
|
||||
copy.imageSubresource.mipLevel = item.level;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.baseArrayLayer = item.baseArrayLayer;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageOffset = {0, 0, 0};
|
||||
copy.imageExtent = {static_cast<Uint32>(item.texelSize.x()), static_cast<Uint32>(item.texelSize.y()),
|
||||
@@ -1066,7 +1130,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
kGraphicsSampledReadStages,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
aspectMask, 0, outResource.mipLevels);
|
||||
aspectMask, 0, outResource.mipLevels, outResource.arrayLayers);
|
||||
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL failed");
|
||||
outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
@@ -1094,7 +1158,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
for (const auto& item : uploadItems) {
|
||||
mipmapTexture.MarkStorageDirty(uploadTarget, item.level, false);
|
||||
mipmapTexture.MarkStorageDirty(item.target, item.level, false);
|
||||
}
|
||||
outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user