mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Feat] (MG_Backend/DirectVulkan): GenerateMipmap WIP
This commit is contained in:
@@ -66,7 +66,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::CopyTexSubImage2D called with null GL context");
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::CopyTexSubImage2D called with null GL context");
|
||||||
pVulkanRenderer->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
|
pVulkanRenderer->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
|
||||||
}
|
}
|
||||||
void GenerateMipmap(GLenum target) {}
|
void GenerateMipmap(GLenum target) {
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GenerateMipmap called with null VulkanRenderer");
|
||||||
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::GenerateMipmap called with null GL context");
|
||||||
|
pVulkanRenderer->GenerateMipmap(target);
|
||||||
|
}
|
||||||
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {}
|
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {}
|
||||||
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
|
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (att.IsTexture()) {
|
if (att.IsTexture()) {
|
||||||
const Int textureLevel = att.GetTextureLevel();
|
const Int textureLevel = att.GetTextureLevel();
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel)));
|
||||||
|
|
||||||
|
Uint64 imageIdentity = 0;
|
||||||
|
auto* texture = att.GetTexture().get();
|
||||||
|
auto* resource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||||
|
if (resource != nullptr) {
|
||||||
|
imageIdentity = reinterpret_cast<Uint64>(resource->image);
|
||||||
|
}
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &imageIdentity, sizeof(imageIdentity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includePendingClear && att.IsTexture()) {
|
if (includePendingClear && att.IsTexture()) {
|
||||||
@@ -94,9 +102,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
currentLayout = m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex);
|
currentLayout = m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto* resource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
auto* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||||
if (resource != nullptr) {
|
if (textureResource != nullptr) {
|
||||||
currentLayout = resource->layout;
|
currentLayout = textureResource->layout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, ¤tLayout, sizeof(currentLayout)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, ¤tLayout, sizeof(currentLayout)));
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#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 <memory>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
static constexpr VkPipelineStageFlags kGraphicsSampledReadStages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
static constexpr VkPipelineStageFlags kGraphicsSampledReadStages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||||
|
|
||||||
@@ -43,6 +45,188 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void GetImageTransitionSourceState(VkImageLayout oldLayout,
|
||||||
|
VkPipelineStageFlags& outSrcStageMask,
|
||||||
|
VkAccessFlags& outSrcAccessMask) {
|
||||||
|
switch (oldLayout) {
|
||||||
|
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
outSrcAccessMask = 0;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_GENERAL:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||||
|
outSrcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
outSrcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||||
|
outSrcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||||
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||||
|
outSrcStageMask = kGraphicsSampledReadStages;
|
||||||
|
outSrcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
|
outSrcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
|
outSrcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
MOBILEGL_ASSERT(false, "GetImageTransitionSourceState: unsupported layout=%d", static_cast<Int>(oldLayout));
|
||||||
|
outSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
outSrcAccessMask = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetImageTransitionDestinationState(VkImageLayout newLayout,
|
||||||
|
VkPipelineStageFlags& outDstStageMask,
|
||||||
|
VkAccessFlags& outDstAccessMask) {
|
||||||
|
switch (newLayout) {
|
||||||
|
case VK_IMAGE_LAYOUT_GENERAL:
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||||
|
outDstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
outDstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||||
|
outDstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||||
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||||
|
outDstStageMask = kGraphicsSampledReadStages;
|
||||||
|
outDstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
|
outDstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
return;
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
|
outDstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
MOBILEGL_ASSERT(false, "GetImageTransitionDestinationState: unsupported layout=%d", static_cast<Int>(newLayout));
|
||||||
|
outDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
outDstAccessMask = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool PreserveTextureContentsOnRecreate(VkDevice device,
|
||||||
|
VkCommandPool commandPool,
|
||||||
|
VkQueue graphicsQueue,
|
||||||
|
const VkTextureManager::TextureResource& oldResource,
|
||||||
|
VkTextureManager::TextureResource& newResource) {
|
||||||
|
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: device is null");
|
||||||
|
MOBILEGL_ASSERT(commandPool != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: commandPool is null");
|
||||||
|
MOBILEGL_ASSERT(graphicsQueue != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: graphicsQueue is null");
|
||||||
|
MOBILEGL_ASSERT(oldResource.image != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: old image is null");
|
||||||
|
MOBILEGL_ASSERT(newResource.image != VK_NULL_HANDLE, "PreserveTextureContentsOnRecreate: new image is null");
|
||||||
|
|
||||||
|
const Uint32 preservedMipLevels = std::min(oldResource.mipLevels, newResource.mipLevels);
|
||||||
|
if (preservedMipLevels == 0 || oldResource.layout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo allocInfo{};
|
||||||
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
allocInfo.commandPool = commandPool;
|
||||||
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
allocInfo.commandBufferCount = 1;
|
||||||
|
|
||||||
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer),
|
||||||
|
"vkAllocateCommandBuffers(texture preserve)");
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo beginInfo{};
|
||||||
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture preserve)");
|
||||||
|
|
||||||
|
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);
|
||||||
|
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to prepare destination image");
|
||||||
|
|
||||||
|
VkImageLayout srcTrackedLayout = oldResource.layout;
|
||||||
|
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags srcAccessMask = 0;
|
||||||
|
GetImageTransitionSourceState(srcTrackedLayout, srcStageMask, srcAccessMask);
|
||||||
|
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);
|
||||||
|
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to prepare source image");
|
||||||
|
|
||||||
|
Vector<VkImageCopy> copyRegions;
|
||||||
|
copyRegions.reserve(preservedMipLevels);
|
||||||
|
for (Uint32 level = 0; level < preservedMipLevels; ++level) {
|
||||||
|
VkImageCopy copy{};
|
||||||
|
copy.srcSubresource.aspectMask = oldResource.aspect;
|
||||||
|
copy.srcSubresource.mipLevel = level;
|
||||||
|
copy.srcSubresource.baseArrayLayer = 0;
|
||||||
|
copy.srcSubresource.layerCount = oldResource.arrayLayers;
|
||||||
|
copy.dstSubresource.aspectMask = newResource.aspect;
|
||||||
|
copy.dstSubresource.mipLevel = level;
|
||||||
|
copy.dstSubresource.baseArrayLayer = 0;
|
||||||
|
copy.dstSubresource.layerCount = newResource.arrayLayers;
|
||||||
|
copy.extent.width = std::max(oldResource.extent.width >> level, 1u);
|
||||||
|
copy.extent.height = std::max(oldResource.extent.height >> level, 1u);
|
||||||
|
copy.extent.depth = std::max(oldResource.depth >> level, 1u);
|
||||||
|
copyRegions.push_back(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdCopyImage(commandBuffer,
|
||||||
|
oldResource.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
newResource.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
static_cast<Uint32>(copyRegions.size()), copyRegions.data());
|
||||||
|
|
||||||
|
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags dstAccessMask = 0;
|
||||||
|
GetImageTransitionDestinationState(oldResource.layout, dstStageMask, dstAccessMask);
|
||||||
|
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);
|
||||||
|
MOBILEGL_ASSERT(ok, "PreserveTextureContentsOnRecreate: failed to restore destination layout");
|
||||||
|
|
||||||
|
VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture preserve)");
|
||||||
|
|
||||||
|
VkSubmitInfo submitInfo{};
|
||||||
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &commandBuffer;
|
||||||
|
|
||||||
|
VkFenceCreateInfo fenceInfo{};
|
||||||
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||||
|
VkFence fence = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateFence(device, &fenceInfo, nullptr, &fence), "vkCreateFence(texture preserve)");
|
||||||
|
VK_VERIFY(vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence), "vkQueueSubmit(texture preserve)");
|
||||||
|
VK_VERIFY(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX), "vkWaitForFences(texture preserve)");
|
||||||
|
|
||||||
|
vkDestroyFence(device, fence, nullptr);
|
||||||
|
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static TextureFormatInfo ResolveTextureFormatInfo(TextureInternalFormat format) {
|
static TextureFormatInfo ResolveTextureFormatInfo(TextureInternalFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case TextureInternalFormat::RGB:
|
case TextureInternalFormat::RGB:
|
||||||
@@ -446,7 +630,23 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Bool preserveExistingContent =
|
||||||
|
resource.image != VK_NULL_HANDLE &&
|
||||||
|
resource.format == format &&
|
||||||
|
resource.extent.width == static_cast<Uint32>(texelSize.x()) &&
|
||||||
|
resource.extent.height == static_cast<Uint32>(texelSize.y()) &&
|
||||||
|
resource.depth == shapeInfo.depth &&
|
||||||
|
resource.arrayLayers == shapeInfo.arrayLayers &&
|
||||||
|
resource.viewType == shapeInfo.viewType &&
|
||||||
|
resource.mipLevels < mipLevels &&
|
||||||
|
resource.layout != VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
|
||||||
|
std::unique_ptr<TextureResource> preservedResource;
|
||||||
|
if (preserveExistingContent) {
|
||||||
|
preservedResource = std::make_unique<TextureResource>(Move(resource));
|
||||||
|
} else {
|
||||||
resource.Reset();
|
resource.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
auto aspect = GetAspectMaskForFormat(format);
|
auto aspect = GetAspectMaskForFormat(format);
|
||||||
|
|
||||||
@@ -489,6 +689,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
resource.aspect = aspect;
|
resource.aspect = aspect;
|
||||||
resource.viewType = shapeInfo.viewType;
|
resource.viewType = shapeInfo.viewType;
|
||||||
resource.syncedTextureParamsVersion = 0;
|
resource.syncedTextureParamsVersion = 0;
|
||||||
|
|
||||||
|
if (preservedResource) {
|
||||||
|
const Bool preserved = PreserveTextureContentsOnRecreate(
|
||||||
|
m_device, m_commandPool, m_graphicsQueue, *preservedResource, resource);
|
||||||
|
MOBILEGL_ASSERT(preserved,
|
||||||
|
"SyncTextureResource: failed to preserve texture contents while growing mip chain");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
#include "MG_State/GLState/ProgramState/ShaderObject.h"
|
#include "MG_State/GLState/ProgramState/ShaderObject.h"
|
||||||
#include "MG_State/GLState/SamplerState/SamplerObject.h"
|
#include "MG_State/GLState/SamplerState/SamplerObject.h"
|
||||||
|
#include "MG_State/GLState/TextureState/TextureObject.h"
|
||||||
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
|
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
|
||||||
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
|
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
|
||||||
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
|
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
|
||||||
@@ -133,6 +134,68 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
static constexpr Uint kHiddenBlitNearestSamplerId = 0xFFFFFFF3u;
|
static constexpr Uint kHiddenBlitNearestSamplerId = 0xFFFFFFF3u;
|
||||||
static constexpr Uint kHiddenBlitLinearSamplerId = 0xFFFFFFF4u;
|
static constexpr Uint kHiddenBlitLinearSamplerId = 0xFFFFFFF4u;
|
||||||
|
|
||||||
|
static Uint32 ComputeFullMipLevelCount(const IntVec3& baseTexelSize) {
|
||||||
|
Int maxDimension = std::max<Int>(
|
||||||
|
baseTexelSize.x(),
|
||||||
|
std::max<Int>(baseTexelSize.y(), std::max<Int>(baseTexelSize.z(), 1)));
|
||||||
|
Uint32 mipLevelCount = 1;
|
||||||
|
while (maxDimension > 1) {
|
||||||
|
maxDimension = std::max<Int>(maxDimension / 2, 1);
|
||||||
|
++mipLevelCount;
|
||||||
|
}
|
||||||
|
return mipLevelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntVec3 ComputeMipTexelSize(const IntVec3& baseTexelSize, Uint32 relativeMipLevel) {
|
||||||
|
const Int width = std::max<Int>(baseTexelSize.x() >> static_cast<Int>(relativeMipLevel), 1);
|
||||||
|
const Int height = std::max<Int>(baseTexelSize.y() >> static_cast<Int>(relativeMipLevel), 1);
|
||||||
|
const Int depth = std::max<Int>(baseTexelSize.z() >> static_cast<Int>(relativeMipLevel), 1);
|
||||||
|
return {width, height, depth};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool EnsureGenerateMipmapStorageAllocated(::MobileGL::MG_State::GLState::TextureObjectMipmap& texture,
|
||||||
|
::MobileGL::TextureUploadTarget uploadTarget,
|
||||||
|
Uint32 baseMipLevel) {
|
||||||
|
const Uint32 existingMipLevelCount = static_cast<Uint32>(texture.GetMipmapLevelCount());
|
||||||
|
if (existingMipLevelCount <= baseMipLevel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, baseMipLevel);
|
||||||
|
const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, baseMipLevel);
|
||||||
|
if (baseTexelSize.x() <= 0 || baseTexelSize.y() <= 0 || baseTexelSize.z() <= 0 || baseByteSize == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SizeT baseTexelCount = static_cast<SizeT>(baseTexelSize.x()) * static_cast<SizeT>(baseTexelSize.y()) *
|
||||||
|
static_cast<SizeT>(baseTexelSize.z());
|
||||||
|
if (baseTexelCount == 0 || (baseByteSize % baseTexelCount) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SizeT bytesPerTexel = baseByteSize / baseTexelCount;
|
||||||
|
const Uint32 requiredMipLevelCount = baseMipLevel + ComputeFullMipLevelCount(baseTexelSize);
|
||||||
|
if (existingMipLevelCount >= requiredMipLevelCount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Uint32 level = existingMipLevelCount; level < requiredMipLevelCount; ++level) {
|
||||||
|
const IntVec3 levelTexelSize = ComputeMipTexelSize(baseTexelSize, level - baseMipLevel);
|
||||||
|
const SizeT levelByteSize = bytesPerTexel * static_cast<SizeT>(levelTexelSize.x()) *
|
||||||
|
static_cast<SizeT>(levelTexelSize.y()) *
|
||||||
|
static_cast<SizeT>(levelTexelSize.z());
|
||||||
|
texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize});
|
||||||
|
texture.MarkStorageDirty(uploadTarget, level, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VkImageLayout ResolveGenerateMipmapFinalLayout(VkImageAspectFlags aspectMask) {
|
||||||
|
return (aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0
|
||||||
|
? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
|
||||||
|
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
}
|
||||||
|
|
||||||
enum class BlitSurfaceTransform : Uint32 {
|
enum class BlitSurfaceTransform : Uint32 {
|
||||||
Identity = 0,
|
Identity = 0,
|
||||||
Rotate90 = 1,
|
Rotate90 = 1,
|
||||||
@@ -1843,6 +1906,181 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
|
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
||||||
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||||
|
const auto uploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||||
|
MOBILEGL_ASSERT(textureTarget == TextureTarget::Texture2D || textureTarget == TextureTarget::Texture3D,
|
||||||
|
"GenerateMipmap currently only supports GL_TEXTURE_2D and GL_TEXTURE_3D.");
|
||||||
|
|
||||||
|
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||||
|
auto texture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
||||||
|
MOBILEGL_ASSERT(texture != nullptr, "GenerateMipmap requires a bound texture.");
|
||||||
|
MOBILEGL_ASSERT(texture->IsComplete(), "GenerateMipmap requires a complete texture.");
|
||||||
|
|
||||||
|
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(texture.get());
|
||||||
|
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires a mipmapped texture object.");
|
||||||
|
|
||||||
|
const Uint32 currentMipLevelCount = static_cast<Uint32>(mipmapTexture->GetMipmapLevelCount());
|
||||||
|
MOBILEGL_ASSERT(currentMipLevelCount > 0, "GenerateMipmap requires level 0 storage.");
|
||||||
|
|
||||||
|
const Uint32 baseMipLevel = std::min(static_cast<Uint32>(texture->GetLevelRange().x()), currentMipLevelCount - 1);
|
||||||
|
|
||||||
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
if (!frame.isCommandRecording) {
|
||||||
|
m_frameContext.BeginCommandRecording();
|
||||||
|
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
|
||||||
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *texture);
|
||||||
|
MOBILEGL_ASSERT(clearReady,
|
||||||
|
"GenerateMipmap: failed to materialize pending clear for textureId=%d",
|
||||||
|
texture->GetExternalIndex());
|
||||||
|
|
||||||
|
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
||||||
|
MOBILEGL_ASSERT(resource != nullptr && resource->image != VK_NULL_HANDLE,
|
||||||
|
"GenerateMipmap failed to sync the backend texture.");
|
||||||
|
|
||||||
|
VkFormatProperties formatProperties{};
|
||||||
|
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle, resource->format, &formatProperties);
|
||||||
|
const VkFormatFeatureFlags optimalTilingFeatures = formatProperties.optimalTilingFeatures;
|
||||||
|
if ((optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0 ||
|
||||||
|
(optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0) {
|
||||||
|
MGLOG_W("GenerateMipmap skipped for textureId=%d because Vulkan format %d does not support blit-based mip generation",
|
||||||
|
texture->GetExternalIndex(), static_cast<Int>(resource->format));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOBILEGL_ASSERT(EnsureGenerateMipmapStorageAllocated(*mipmapTexture, uploadTarget, baseMipLevel),
|
||||||
|
"GenerateMipmap could not allocate a full mip chain for this texture.");
|
||||||
|
|
||||||
|
resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
||||||
|
MOBILEGL_ASSERT(resource != nullptr && resource->image != VK_NULL_HANDLE,
|
||||||
|
"GenerateMipmap failed to resync the backend texture after allocating mip storage.");
|
||||||
|
MOBILEGL_ASSERT(resource->layout != VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
"GenerateMipmap requires initialized base-level image data.");
|
||||||
|
|
||||||
|
const IntVec3 storageBaseTexelSize = {
|
||||||
|
static_cast<Int>(resource->extent.width),
|
||||||
|
static_cast<Int>(resource->extent.height),
|
||||||
|
static_cast<Int>(resource->depth),
|
||||||
|
};
|
||||||
|
const IntVec3 baseTexelSize = ComputeMipTexelSize(storageBaseTexelSize, baseMipLevel);
|
||||||
|
const Uint32 requiredMipLevelCount = baseMipLevel + ComputeFullMipLevelCount(baseTexelSize);
|
||||||
|
const Uint32 generateMipLevelCount = std::min(requiredMipLevelCount, resource->mipLevels);
|
||||||
|
if (generateMipLevelCount <= baseMipLevel + 1) {
|
||||||
|
resource->layout = ResolveGenerateMipmapFinalLayout(resource->aspect);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkImageLayout originalLayout = resource->layout;
|
||||||
|
const VkImageLayout finalLayout = ResolveGenerateMipmapFinalLayout(resource->aspect);
|
||||||
|
const VkFilter blitFilter = (optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0
|
||||||
|
? VK_FILTER_LINEAR
|
||||||
|
: VK_FILTER_NEAREST;
|
||||||
|
|
||||||
|
VkPipelineStageFlags originalSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags originalSrcAccessMask = 0;
|
||||||
|
GetImageTransitionSourceState(originalLayout, originalSrcStageMask, originalSrcAccessMask);
|
||||||
|
|
||||||
|
VkPipelineStageFlags finalDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags finalDstAccessMask = 0;
|
||||||
|
GetImageTransitionDestinationState(finalLayout, finalDstStageMask, finalDstAccessMask);
|
||||||
|
|
||||||
|
if (originalLayout != finalLayout) {
|
||||||
|
if (baseMipLevel > 0) {
|
||||||
|
VkImageLayout lowerMipLayout = originalLayout;
|
||||||
|
const Bool lowerReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, lowerMipLayout, finalLayout,
|
||||||
|
originalSrcStageMask, finalDstStageMask,
|
||||||
|
originalSrcAccessMask, finalDstAccessMask,
|
||||||
|
resource->aspect, 0, baseMipLevel);
|
||||||
|
MOBILEGL_ASSERT(lowerReady, "%s: failed to transition lower untouched mip levels", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateMipLevelCount < resource->mipLevels) {
|
||||||
|
VkImageLayout upperMipLayout = originalLayout;
|
||||||
|
const Bool upperReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, upperMipLayout, finalLayout,
|
||||||
|
originalSrcStageMask, finalDstStageMask,
|
||||||
|
originalSrcAccessMask, finalDstAccessMask,
|
||||||
|
resource->aspect, generateMipLevelCount, resource->mipLevels - generateMipLevelCount);
|
||||||
|
MOBILEGL_ASSERT(upperReady, "%s: failed to transition upper untouched mip levels", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageLayout srcMipLayout = originalLayout;
|
||||||
|
Bool srcReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, srcMipLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
originalSrcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
originalSrcAccessMask, VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
resource->aspect, baseMipLevel, 1);
|
||||||
|
MOBILEGL_ASSERT(srcReady, "%s: failed to transition base mip level to transfer source", __func__);
|
||||||
|
|
||||||
|
for (Uint32 level = baseMipLevel + 1; level < generateMipLevelCount; ++level) {
|
||||||
|
VkImageLayout dstMipLayout = originalLayout;
|
||||||
|
Bool dstReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, dstMipLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
originalSrcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
originalSrcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
resource->aspect, level, 1);
|
||||||
|
MOBILEGL_ASSERT(dstReady, "%s: failed to transition mip level %u to transfer destination", __func__, level);
|
||||||
|
|
||||||
|
const IntVec3 srcTexelSize = ComputeMipTexelSize(storageBaseTexelSize, level - 1);
|
||||||
|
const IntVec3 dstTexelSize = ComputeMipTexelSize(storageBaseTexelSize, level);
|
||||||
|
|
||||||
|
VkImageBlit blitRegion{};
|
||||||
|
blitRegion.srcSubresource.aspectMask = resource->aspect;
|
||||||
|
blitRegion.srcSubresource.mipLevel = level - 1;
|
||||||
|
blitRegion.srcSubresource.baseArrayLayer = 0;
|
||||||
|
blitRegion.srcSubresource.layerCount = 1;
|
||||||
|
blitRegion.srcOffsets[0] = {0, 0, 0};
|
||||||
|
blitRegion.srcOffsets[1] = {srcTexelSize.x(), srcTexelSize.y(), srcTexelSize.z()};
|
||||||
|
blitRegion.dstSubresource.aspectMask = resource->aspect;
|
||||||
|
blitRegion.dstSubresource.mipLevel = level;
|
||||||
|
blitRegion.dstSubresource.baseArrayLayer = 0;
|
||||||
|
blitRegion.dstSubresource.layerCount = 1;
|
||||||
|
blitRegion.dstOffsets[0] = {0, 0, 0};
|
||||||
|
blitRegion.dstOffsets[1] = {dstTexelSize.x(), dstTexelSize.y(), dstTexelSize.z()};
|
||||||
|
|
||||||
|
vkCmdBlitImage(frame.commandBuffer,
|
||||||
|
resource->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
1, &blitRegion, blitFilter);
|
||||||
|
|
||||||
|
VkImageLayout finishedSrcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
Bool srcRestored = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, finishedSrcLayout, finalLayout,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, finalDstStageMask,
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT, finalDstAccessMask,
|
||||||
|
resource->aspect, level - 1, 1);
|
||||||
|
MOBILEGL_ASSERT(srcRestored, "%s: failed to transition mip level %u to final layout", __func__, level - 1);
|
||||||
|
|
||||||
|
if (level + 1 < generateMipLevelCount) {
|
||||||
|
VkImageLayout nextSrcLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
Bool nextSrcReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, nextSrcLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
resource->aspect, level, 1);
|
||||||
|
MOBILEGL_ASSERT(nextSrcReady, "%s: failed to prepare mip level %u as next transfer source", __func__, level);
|
||||||
|
} else {
|
||||||
|
VkImageLayout lastMipLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
Bool lastMipReady = VkTextureManager::TransitionImageLayout(
|
||||||
|
frame.commandBuffer, resource->image, lastMipLayout, finalLayout,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, finalDstStageMask,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT, finalDstAccessMask,
|
||||||
|
resource->aspect, level, 1);
|
||||||
|
MOBILEGL_ASSERT(lastMipReady, "%s: failed to transition last mip level to final layout", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource->layout = finalLayout;
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanRenderer::DrawArrays(const DrawCmd& payload) {
|
void VulkanRenderer::DrawArrays(const DrawCmd& payload) {
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
GLbitfield mask, GLenum filter);
|
GLbitfield mask, GLenum filter);
|
||||||
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
||||||
GLint x, GLint y, GLsizei width, GLsizei height);
|
GLint x, GLint y, GLsizei width, GLsizei height);
|
||||||
|
void GenerateMipmap(GLenum target);
|
||||||
void DrawArrays(const DrawCmd& payload);
|
void DrawArrays(const DrawCmd& payload);
|
||||||
void DrawElements(const DrawIndexedCmd& payload);
|
void DrawElements(const DrawIndexedCmd& payload);
|
||||||
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace MobileGL {
|
|||||||
case TextureInternalFormat::RG8Snorm:
|
case TextureInternalFormat::RG8Snorm:
|
||||||
case TextureInternalFormat::RG8I:
|
case TextureInternalFormat::RG8I:
|
||||||
case TextureInternalFormat::RG8UI:
|
case TextureInternalFormat::RG8UI:
|
||||||
|
case TextureInternalFormat::DepthComponent16:
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
case TextureInternalFormat::RGB4:
|
case TextureInternalFormat::RGB4:
|
||||||
@@ -39,6 +40,7 @@ namespace MobileGL {
|
|||||||
case TextureInternalFormat::SRGB8:
|
case TextureInternalFormat::SRGB8:
|
||||||
case TextureInternalFormat::RGB8I:
|
case TextureInternalFormat::RGB8I:
|
||||||
case TextureInternalFormat::RGB8UI:
|
case TextureInternalFormat::RGB8UI:
|
||||||
|
case TextureInternalFormat::DepthComponent24:
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
case TextureInternalFormat::RGBA2:
|
case TextureInternalFormat::RGBA2:
|
||||||
@@ -52,6 +54,11 @@ namespace MobileGL {
|
|||||||
case TextureInternalFormat::RGB10A2:
|
case TextureInternalFormat::RGB10A2:
|
||||||
case TextureInternalFormat::RGB10A2UI:
|
case TextureInternalFormat::RGB10A2UI:
|
||||||
case TextureInternalFormat::R32F:
|
case TextureInternalFormat::R32F:
|
||||||
|
case TextureInternalFormat::DepthComponent:
|
||||||
|
case TextureInternalFormat::DepthComponent32:
|
||||||
|
case TextureInternalFormat::DepthComponent32F:
|
||||||
|
case TextureInternalFormat::DepthStencil:
|
||||||
|
case TextureInternalFormat::Depth24Stencil8:
|
||||||
|
|
||||||
case TextureInternalFormat::RG16:
|
case TextureInternalFormat::RG16:
|
||||||
case TextureInternalFormat::RG16Snorm:
|
case TextureInternalFormat::RG16Snorm:
|
||||||
@@ -83,6 +90,7 @@ namespace MobileGL {
|
|||||||
case TextureInternalFormat::RGBA32F:
|
case TextureInternalFormat::RGBA32F:
|
||||||
case TextureInternalFormat::RGBA32I:
|
case TextureInternalFormat::RGBA32I:
|
||||||
case TextureInternalFormat::RGBA32UI:
|
case TextureInternalFormat::RGBA32UI:
|
||||||
|
case TextureInternalFormat::Depth32FStencil8:
|
||||||
return 16;
|
return 16;
|
||||||
|
|
||||||
case TextureInternalFormat::R11FG11FB10F:
|
case TextureInternalFormat::R11FG11FB10F:
|
||||||
@@ -125,7 +133,13 @@ namespace MobileGL {
|
|||||||
SizeT GetBaseInternalFormatComponentCount(TextureInternalFormat format) {
|
SizeT GetBaseInternalFormatComponentCount(TextureInternalFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case TextureInternalFormat::DepthComponent:
|
case TextureInternalFormat::DepthComponent:
|
||||||
|
case TextureInternalFormat::DepthComponent16:
|
||||||
|
case TextureInternalFormat::DepthComponent24:
|
||||||
|
case TextureInternalFormat::DepthComponent32:
|
||||||
|
case TextureInternalFormat::DepthComponent32F:
|
||||||
case TextureInternalFormat::DepthStencil:
|
case TextureInternalFormat::DepthStencil:
|
||||||
|
case TextureInternalFormat::Depth24Stencil8:
|
||||||
|
case TextureInternalFormat::Depth32FStencil8:
|
||||||
// Depth stencil is actually 2 components
|
// Depth stencil is actually 2 components
|
||||||
// tho real formats always gives byte size in whole
|
// tho real formats always gives byte size in whole
|
||||||
// so we count this as one here
|
// so we count this as one here
|
||||||
|
|||||||
Reference in New Issue
Block a user