mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Feat] (MG_Backend/DirectVulkan): implement texture mipmap
This commit is contained in:
@@ -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};
|
||||||
|
|||||||
Reference in New Issue
Block a user