[Feat] (MG_Backend/DirectVulkan): implement texture mipmap

This commit is contained in:
2026-03-09 15:09:05 +08:00
parent 67069d4a12
commit e82815802e
5 changed files with 177 additions and 46 deletions
@@ -532,7 +532,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
outImageInfo = { outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse), .sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse),
.imageView = resource->view, .imageView = resource->fullView,
.imageLayout = resource->layout, .imageLayout = resource->layout,
}; };
return outImageInfo.sampler != VK_NULL_HANDLE; return outImageInfo.sampler != VK_NULL_HANDLE;
@@ -554,7 +554,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
outImageInfo = { outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler), .sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler),
.imageView = resource->view, .imageView = resource->fullView,
.imageLayout = resource->layout, .imageLayout = resource->layout,
}; };
return outImageInfo.sampler != VK_NULL_HANDLE; return outImageInfo.sampler != VK_NULL_HANDLE;
@@ -66,6 +66,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
else if (att.IsRenderbuffer()) else if (att.IsRenderbuffer())
contentPtr = att.GetRenderbuffer().get(); contentPtr = att.GetRenderbuffer().get();
XXHASH_VERIFY(XXH64_update(m_hashState, &contentPtr, sizeof(contentPtr))); XXHASH_VERIFY(XXH64_update(m_hashState, &contentPtr, sizeof(contentPtr)));
if (att.IsTexture()) {
const Int textureLevel = att.GetTextureLevel();
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel)));
}
if (includePendingClear && att.IsTexture()) { if (includePendingClear && att.IsTexture()) {
auto* texture = att.GetTexture().get(); auto* texture = att.GetTexture().get();
@@ -158,6 +162,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto& att = fbo.GetAttachment(drawbuf); auto& att = fbo.GetAttachment(drawbuf);
auto* texture = att.GetTexture().get(); auto* texture = att.GetTexture().get();
const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(att.GetTextureLevel(), 0));
const auto textureTarget = texture->GetTarget(); const auto textureTarget = texture->GetTarget();
// Color attachment description // Color attachment description
@@ -190,9 +195,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}); });
} }
if (width == 0) if (width == 0)
width = texture2d->GetBaseSize().x(); width = att.GetSize().x();
if (height == 0) if (height == 0)
height = texture2d->GetBaseSize().y(); height = att.GetSize().y();
if (isDefaultFbo) { if (isDefaultFbo) {
const auto& swapchainViews = m_swapchainObject.GetImageViews(); const auto& swapchainViews = m_swapchainObject.GetImageViews();
@@ -215,7 +220,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.texture = texture, .texture = texture,
.finalLayout = desc.finalLayout, .finalLayout = desc.finalLayout,
}); });
attachmentViews[i] = textureResources[i]->view; attachmentViews[i] = m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel);
MOBILEGL_ASSERT(attachmentViews[i] != VK_NULL_HANDLE,
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at color attachment %d", i);
} }
if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
@@ -249,6 +256,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkTextureManager::TextureResource* depthTextureResource = nullptr; VkTextureManager::TextureResource* depthTextureResource = nullptr;
if (depthAtt.IsComplete() && depthAtt.IsTexture()) { if (depthAtt.IsComplete() && depthAtt.IsTexture()) {
auto& texture = *depthAtt.GetTexture(); auto& texture = *depthAtt.GetTexture();
const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(depthAtt.GetTextureLevel(), 0));
const Uint32 depthAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size()); const Uint32 depthAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
ClearAttachmentPayload clearPayload{}; ClearAttachmentPayload clearPayload{};
Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload); Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload);
@@ -311,11 +319,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.finalLayout = depthAttachmentDescription.finalLayout, .finalLayout = depthAttachmentDescription.finalLayout,
}); });
textureResources.emplace_back(depthTextureResource); textureResources.emplace_back(depthTextureResource);
attachmentViews.emplace_back(depthTextureResource->view); attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(texture, attachmentMipLevel));
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment");
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
auto texture2d = static_cast<MG_State::GLState::TextureObject2D*>(&texture); width = depthAtt.GetSize().x();
width = texture2d->GetBaseSize().x(); height = depthAtt.GetSize().y();
height = texture2d->GetBaseSize().y();
} }
} }
attachmentDescriptions.emplace_back(depthAttachmentDescription); attachmentDescriptions.emplace_back(depthAttachmentDescription);
@@ -74,6 +74,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return &(it->second); return &(it->second);
} }
VkImageView VkTextureManager::GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel) {
TextureResource* resource = SyncTextureAndGetDescriptor(texture);
if (resource == nullptr || resource->image == VK_NULL_HANDLE || mipLevel >= resource->mipLevels) {
return VK_NULL_HANDLE;
}
if (resource->perMipViews.size() != resource->mipLevels) {
resource->perMipViews.resize(resource->mipLevels, VK_NULL_HANDLE);
}
VkImageView& perMipView = resource->perMipViews[mipLevel];
if (perMipView != VK_NULL_HANDLE) {
return perMipView;
}
perMipView = CreateImageView(resource->image, resource->format, resource->aspect, mipLevel, 1);
if (perMipView == VK_NULL_HANDLE) {
MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), mipLevel);
return VK_NULL_HANDLE;
}
return perMipView;
}
void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) { void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) {
MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null"); MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null");
auto it = m_textureResources.find(texture); auto it = m_textureResources.find(texture);
@@ -127,7 +151,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask, const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask,
kGraphicsSampledReadStages, srcAccessMask, kGraphicsSampledReadStages, srcAccessMask,
VK_ACCESS_SHADER_READ_BIT, resource->aspect); VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels);
MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex()); MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex());
return ok; return ok;
} }
@@ -136,7 +160,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkImageLayout& trackedLayout, VkImageLayout newLayout, VkImageLayout& trackedLayout, VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
VkImageAspectFlags aspectMask) { VkImageAspectFlags aspectMask, Uint32 baseMipLevel, Uint32 levelCount) {
MOBILEGL_ASSERT(image != VK_NULL_HANDLE, "TransitionImageLayout: m_image == VK_NULL_HANDLE"); MOBILEGL_ASSERT(image != VK_NULL_HANDLE, "TransitionImageLayout: m_image == VK_NULL_HANDLE");
MOBILEGL_ASSERT(!((dstAccessMask & VK_ACCESS_TRANSFER_READ_BIT) != 0 && MOBILEGL_ASSERT(!((dstAccessMask & VK_ACCESS_TRANSFER_READ_BIT) != 0 &&
(dstStageMask & VK_PIPELINE_STAGE_TRANSFER_BIT) == 0), (dstStageMask & VK_PIPELINE_STAGE_TRANSFER_BIT) == 0),
@@ -158,8 +182,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image; barrier.image = image;
barrier.subresourceRange.aspectMask = aspectMask; barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.baseMipLevel = baseMipLevel;
barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier); vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
@@ -205,6 +229,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_D("%s: SyncTextureResource failed", __func__); MGLOG_D("%s: SyncTextureResource failed", __func__);
return false; return false;
} }
if (!SyncTextureViews(texture, outResource)) {
MGLOG_D("%s: SyncTextureViews failed", __func__);
return false;
}
Bool hasDirtyMipLevel = false; Bool hasDirtyMipLevel = false;
for (Uint32 level = 0; level < mipLevelCount; ++level) { for (Uint32 level = 0; level < mipLevelCount; ++level) {
@@ -251,6 +279,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
resource.extent.height == static_cast<Uint32>(texelSize.y()) && resource.extent.height == static_cast<Uint32>(texelSize.y()) &&
resource.mipLevels == mipLevels; resource.mipLevels == mipLevels;
if (compatible) { if (compatible) {
if (resource.perMipViews.size() != mipLevels) {
resource.perMipViews.resize(mipLevels, VK_NULL_HANDLE);
}
return true; return true;
} }
@@ -283,26 +314,68 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr), VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
"vmaCreateImage(texture)"); "vmaCreateImage(texture)");
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = resource.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspect;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = mipLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &resource.view), "vkCreateImageView(texture)");
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED; resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())}; resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
resource.mipLevels = mipLevels; resource.mipLevels = mipLevels;
resource.perMipViews.assign(mipLevels, VK_NULL_HANDLE);
resource.sampledBaseMipLevel = 0;
resource.sampledLevelCount = mipLevels;
resource.format = format; resource.format = format;
resource.aspect = viewInfo.subresourceRange.aspectMask; resource.aspect = aspect;
resource.syncedTextureParamsVersion = 0;
return true; return true;
} }
Bool VkTextureManager::SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource) {
MOBILEGL_ASSERT(resource.image != VK_NULL_HANDLE, "SyncTextureViews: image == VK_NULL_HANDLE");
Uint32 baseMipLevel = 0;
Uint32 levelCount = 1;
ResolveViewMipRange(texture, resource.mipLevels, baseMipLevel, levelCount);
const Bool needsRecreate =
resource.fullView == VK_NULL_HANDLE ||
resource.sampledBaseMipLevel != baseMipLevel ||
resource.sampledLevelCount != levelCount ||
resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion();
if (!needsRecreate) {
return true;
}
if (resource.fullView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, resource.fullView, nullptr);
resource.fullView = VK_NULL_HANDLE;
}
resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, baseMipLevel, levelCount);
if (resource.fullView == VK_NULL_HANDLE) {
return false;
}
resource.sampledBaseMipLevel = baseMipLevel;
resource.sampledLevelCount = levelCount;
resource.syncedTextureParamsVersion = texture.GetTextureParamsVersion();
return true;
}
VkImageView VkTextureManager::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
Uint32 baseMipLevel, Uint32 levelCount) const {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspect;
viewInfo.subresourceRange.baseMipLevel = baseMipLevel;
viewInfo.subresourceRange.levelCount = levelCount;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &view), "vkCreateImageView(texture)");
return view;
}
Bool VkTextureManager::UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture, Bool VkTextureManager::UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
TextureUploadTarget uploadTarget, TextureUploadTarget uploadTarget,
TextureResource &outResource) { TextureResource &outResource) {
@@ -390,7 +463,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
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, outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_ACCESS_SHADER_READ_BIT : 0,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
aspectMask); aspectMask, 0, outResource.mipLevels);
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed"); MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
for (const auto& item : uploadItems) { for (const auto& item : uploadItems) {
@@ -415,7 +488,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
kGraphicsSampledReadStages, kGraphicsSampledReadStages,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_READ_BIT,
aspectMask); aspectMask, 0, outResource.mipLevels);
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL failed"); MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL failed");
outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@@ -472,15 +545,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
continue; continue;
} }
const auto level0TexelSize = mipTexture->GetMipmapTexelSize(target, 0); // Backing VkImage allocation still uses storage mip 0 as the physical image extent.
const auto level0ByteSize = mipTexture->GetMipmapByteSize(target, 0); // GL_TEXTURE_BASE_LEVEL / MAX_LEVEL are applied later when building the sampled view.
if (level0TexelSize.x() <= 0 || level0TexelSize.y() <= 0 /*|| level0ByteSize == 0*/) { const auto storageBaseTexelSize = mipTexture->GetMipmapTexelSize(target, 0);
const auto storageBaseByteSize = mipTexture->GetMipmapByteSize(target, 0);
if (storageBaseTexelSize.x() <= 0 || storageBaseTexelSize.y() <= 0 /*|| storageBaseByteSize == 0*/) {
continue; continue;
} }
outTarget = target; outTarget = target;
outTexelSize = level0TexelSize; outTexelSize = storageBaseTexelSize;
outByteSize = level0ByteSize; outByteSize = storageBaseByteSize;
outMipLevelCount = mipLevelCount; outMipLevelCount = mipLevelCount;
return true; return true;
} }
@@ -507,6 +582,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return validLevelCount; return validLevelCount;
} }
void VkTextureManager::ResolveViewMipRange(const MG_State::GLState::ITextureObject& texture, Uint32 mipLevels,
Uint32& outBaseMipLevel, Uint32& outLevelCount) {
MOBILEGL_ASSERT(mipLevels > 0, "ResolveViewMipRange: mipLevels must be > 0");
const auto& levelRange = texture.GetLevelRange();
const Uint32 maxAvailableMipLevel = mipLevels - 1;
const Uint32 requestedBaseMipLevel = std::min(static_cast<Uint32>(levelRange.x()), maxAvailableMipLevel);
Uint32 requestedMaxMipLevel = std::min(static_cast<Uint32>(levelRange.y()), maxAvailableMipLevel);
if (requestedMaxMipLevel < requestedBaseMipLevel) {
requestedMaxMipLevel = requestedBaseMipLevel;
}
outBaseMipLevel = requestedBaseMipLevel;
outLevelCount = requestedMaxMipLevel - requestedBaseMipLevel + 1;
}
VkImageAspectFlags VkTextureManager::GetAspectMaskForFormat(VkFormat format) { VkImageAspectFlags VkTextureManager::GetAspectMaskForFormat(VkFormat format) {
switch (format) { switch (format) {
case VK_FORMAT_D16_UNORM: case VK_FORMAT_D16_UNORM:
@@ -31,41 +31,58 @@ public:
struct TextureResource { struct TextureResource {
VkImage image = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr; VmaAllocation allocation = nullptr;
VkImageView view = VK_NULL_HANDLE; VkImageView fullView = VK_NULL_HANDLE;
Vector<VkImageView> perMipViews;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0}; VkExtent2D extent = {0, 0};
Uint32 mipLevels = 1; Uint32 mipLevels = 1;
Uint32 sampledBaseMipLevel = 0;
Uint32 sampledLevelCount = 1;
VkFormat format = VK_FORMAT_UNDEFINED; VkFormat format = VK_FORMAT_UNDEFINED;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE; VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE;
Uint16 syncedTextureParamsVersion = 0;
TextureResource() = default; TextureResource() = default;
TextureResource(const TextureResource&) = delete; TextureResource(const TextureResource&) = delete;
TextureResource(TextureResource&& that) noexcept { TextureResource(TextureResource&& that) noexcept {
std::swap(this->image, that.image); std::swap(this->image, that.image);
std::swap(this->allocation, that.allocation); std::swap(this->allocation, that.allocation);
std::swap(this->view, that.view); std::swap(this->fullView, that.fullView);
std::swap(this->perMipViews, that.perMipViews);
std::swap(this->layout, that.layout); std::swap(this->layout, that.layout);
std::swap(this->extent, that.extent); std::swap(this->extent, that.extent);
std::swap(this->mipLevels, that.mipLevels); std::swap(this->mipLevels, that.mipLevels);
std::swap(this->sampledBaseMipLevel, that.sampledBaseMipLevel);
std::swap(this->sampledLevelCount, that.sampledLevelCount);
std::swap(this->format, that.format); std::swap(this->format, that.format);
std::swap(this->aspect, that.aspect); std::swap(this->aspect, that.aspect);
std::swap(this->syncedTextureParamsVersion, that.syncedTextureParamsVersion);
} }
void Reset() { void Reset() {
if (view != VK_NULL_HANDLE) { if (fullView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, view, nullptr); vkDestroyImageView(s_device, fullView, nullptr);
}
for (const auto attachmentView : perMipViews) {
if (attachmentView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, attachmentView, nullptr);
}
} }
if (image != VK_NULL_HANDLE && allocation != nullptr) { if (image != VK_NULL_HANDLE && allocation != nullptr) {
vmaDestroyImage(s_allocator, image, allocation); vmaDestroyImage(s_allocator, image, allocation);
} }
view = VK_NULL_HANDLE; fullView = VK_NULL_HANDLE;
perMipViews.clear();
image = VK_NULL_HANDLE; image = VK_NULL_HANDLE;
allocation = nullptr; allocation = nullptr;
layout = VK_IMAGE_LAYOUT_UNDEFINED; layout = VK_IMAGE_LAYOUT_UNDEFINED;
extent = {0, 0}; extent = {0, 0};
mipLevels = 1; mipLevels = 1;
sampledBaseMipLevel = 0;
sampledLevelCount = 1;
format = VK_FORMAT_UNDEFINED; format = VK_FORMAT_UNDEFINED;
aspect = VK_IMAGE_ASPECT_NONE; aspect = VK_IMAGE_ASPECT_NONE;
syncedTextureParamsVersion = 0;
} }
~TextureResource() { ~TextureResource() {
@@ -81,13 +98,15 @@ public:
TextureResource* SyncTextureAndGetDescriptor( TextureResource* SyncTextureAndGetDescriptor(
MG_State::GLState::ITextureObject& texture); MG_State::GLState::ITextureObject& texture);
VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel);
void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout); void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout);
Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask); VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask,
Uint32 baseMipLevel = 0, Uint32 levelCount = 1);
SizeT CollectGarbage(); SizeT CollectGarbage();
private: private:
@@ -98,6 +117,9 @@ private:
TextureUploadTarget uploadTarget, TextureUploadTarget uploadTarget,
const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels, const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels,
TextureResource &resource); TextureResource &resource);
Bool SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource);
VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
Uint32 baseMipLevel, Uint32 levelCount) const;
Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture, Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
TextureUploadTarget uploadTarget, TextureUploadTarget uploadTarget,
TextureResource &outResource); TextureResource &outResource);
@@ -107,6 +129,8 @@ private:
SizeT& outByteSize, SizeT& outByteSize,
Uint32& outMipLevelCount); Uint32& outMipLevelCount);
static Uint32 GetUploadMipLevelCount(const MG_State::GLState::TextureObjectMipmap& texture, TextureUploadTarget target); static Uint32 GetUploadMipLevelCount(const MG_State::GLState::TextureObjectMipmap& texture, TextureUploadTarget target);
static void ResolveViewMipRange(const MG_State::GLState::ITextureObject& texture, Uint32 mipLevels,
Uint32& outBaseMipLevel, Uint32& outLevelCount);
static VkImageAspectFlags GetAspectMaskForFormat(VkFormat format); static VkImageAspectFlags GetAspectMaskForFormat(VkFormat format);
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
@@ -93,6 +93,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkImageLayout* trackedLayout = nullptr; VkImageLayout* trackedLayout = nullptr;
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_NONE; VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_NONE;
IntVec2 extent = {0, 0}; IntVec2 extent = {0, 0};
Uint32 mipLevel = 0;
Uint32 mipLevelCount = 1;
const char* label = nullptr; const char* label = nullptr;
}; };
@@ -164,6 +166,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
const auto extent = swapchainObject.GetExtent(); const auto extent = swapchainObject.GetExtent();
outBinding.extent = {static_cast<Int>(extent.width), static_cast<Int>(extent.height)}; outBinding.extent = {static_cast<Int>(extent.width), static_cast<Int>(extent.height)};
outBinding.mipLevel = 0;
outBinding.mipLevelCount = 1;
return true; return true;
} }
@@ -182,7 +186,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
outBinding.image = resource->image; outBinding.image = resource->image;
outBinding.trackedLayout = &resource->layout; outBinding.trackedLayout = &resource->layout;
outBinding.aspectMask = resource->aspect; outBinding.aspectMask = resource->aspect;
outBinding.extent = {static_cast<Int>(resource->extent.width), static_cast<Int>(resource->extent.height)}; const auto attachmentExtent = attachment.GetSize();
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
outBinding.mipLevelCount = resource->mipLevels;
return true; return true;
} }
@@ -875,7 +882,7 @@ void main() {
Bool ok = VkTextureManager::TransitionImageLayout( Bool ok = VkTextureManager::TransitionImageLayout(
commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
resource->aspect); resource->aspect, 0, resource->mipLevels);
MOBILEGL_ASSERT(ok, MOBILEGL_ASSERT(ok,
"MaterializePendingClearForTexture: failed to transition textureId=%d to TRANSFER_DST", "MaterializePendingClearForTexture: failed to transition textureId=%d to TRANSFER_DST",
texture.GetExternalIndex()); texture.GetExternalIndex());
@@ -908,7 +915,7 @@ void main() {
ok = VkTextureManager::TransitionImageLayout( ok = VkTextureManager::TransitionImageLayout(
commandBuffer, resource->image, resource->layout, sampledLayout, commandBuffer, resource->image, resource->layout, sampledLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, resource->aspect); VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels);
MOBILEGL_ASSERT(ok, MOBILEGL_ASSERT(ok,
"MaterializePendingClearForTexture: failed to transition textureId=%d to sampled layout", "MaterializePendingClearForTexture: failed to transition textureId=%d to sampled layout",
texture.GetExternalIndex()); texture.GetExternalIndex());
@@ -1138,7 +1145,7 @@ void main() {
Bool ok = VkTextureManager::TransitionImageLayout( Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask); srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask, 0, srcBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to transition source image", __func__); MOBILEGL_ASSERT(ok, "%s: failed to transition source image", __func__);
} }
@@ -1156,19 +1163,19 @@ void main() {
Bool ok = VkTextureManager::TransitionImageLayout( Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstBinding.aspectMask); dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstBinding.aspectMask, 0, dstBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to transition destination image", __func__); MOBILEGL_ASSERT(ok, "%s: failed to transition destination image", __func__);
} }
VkImageBlit blitRegion{}; VkImageBlit blitRegion{};
blitRegion.srcSubresource.aspectMask = srcBinding.aspectMask; blitRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
blitRegion.srcSubresource.mipLevel = 0; blitRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
blitRegion.srcSubresource.baseArrayLayer = 0; blitRegion.srcSubresource.baseArrayLayer = 0;
blitRegion.srcSubresource.layerCount = 1; blitRegion.srcSubresource.layerCount = 1;
blitRegion.srcOffsets[0] = {srcX0, srcY0, 0}; blitRegion.srcOffsets[0] = {srcX0, srcY0, 0};
blitRegion.srcOffsets[1] = {srcX1, srcY1, 1}; blitRegion.srcOffsets[1] = {srcX1, srcY1, 1};
blitRegion.dstSubresource.aspectMask = dstBinding.aspectMask; blitRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
blitRegion.dstSubresource.mipLevel = 0; blitRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
blitRegion.dstSubresource.baseArrayLayer = 0; blitRegion.dstSubresource.baseArrayLayer = 0;
blitRegion.dstSubresource.layerCount = 1; blitRegion.dstSubresource.layerCount = 1;
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0}; blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};