mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (MG_Util/PixelStoreProcessor, MG_Backend/DirectVulkan): normalize packed RGBA uploads
This commit is contained in:
@@ -15,6 +15,51 @@
|
|||||||
#include "MG_Util/Metrics/TextureMetrics.h"
|
#include "MG_Util/Metrics/TextureMetrics.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
static Bool TryResolveSampleCountFlagBits(Int requestedSamples, VkSampleCountFlagBits& outSampleCount) {
|
||||||
|
switch (requestedSamples <= 0 ? 1 : requestedSamples) {
|
||||||
|
case 1:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
return true;
|
||||||
|
case 2:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_2_BIT;
|
||||||
|
return true;
|
||||||
|
case 4:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_4_BIT;
|
||||||
|
return true;
|
||||||
|
case 8:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_8_BIT;
|
||||||
|
return true;
|
||||||
|
case 16:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_16_BIT;
|
||||||
|
return true;
|
||||||
|
case 32:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_32_BIT;
|
||||||
|
return true;
|
||||||
|
case 64:
|
||||||
|
outSampleCount = VK_SAMPLE_COUNT_64_BIT;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VkImageAspectFlags ResolveImageAspectMaskForFormat(VkFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case VK_FORMAT_D16_UNORM:
|
||||||
|
case VK_FORMAT_X8_D24_UNORM_PACK32:
|
||||||
|
case VK_FORMAT_D32_SFLOAT:
|
||||||
|
return VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||||
|
case VK_FORMAT_S8_UINT:
|
||||||
|
return VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||||
|
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||||
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||||
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||||
|
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||||
|
default:
|
||||||
|
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
|
static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
|
||||||
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
|
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
@@ -112,15 +157,36 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return {attachmentExtent.x(), attachmentExtent.y()};
|
return {attachmentExtent.x(), attachmentExtent.y()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VkRenderPassManager::RenderbufferResource::Destroy(VkDevice device, VmaAllocator allocator) {
|
||||||
|
if (view != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyImageView(device, view, nullptr);
|
||||||
|
}
|
||||||
|
if (image != VK_NULL_HANDLE && allocation != nullptr) {
|
||||||
|
vmaDestroyImage(allocator, image, allocation);
|
||||||
|
}
|
||||||
|
renderbuffer.reset();
|
||||||
|
image = VK_NULL_HANDLE;
|
||||||
|
allocation = nullptr;
|
||||||
|
view = VK_NULL_HANDLE;
|
||||||
|
layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
format = VK_FORMAT_UNDEFINED;
|
||||||
|
aspect = VK_IMAGE_ASPECT_NONE;
|
||||||
|
extent = {0, 0};
|
||||||
|
sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
internalFormat = TextureInternalFormat::Unknown;
|
||||||
|
samples = 0;
|
||||||
|
}
|
||||||
|
|
||||||
VkRenderPassManager::VkRenderPassManager(VkDevice device,
|
VkRenderPassManager::VkRenderPassManager(VkDevice device,
|
||||||
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
|
VkPhysicalDevice physicalDevice, VmaAllocator allocator, const VulkanRendererConfig& config,
|
||||||
SwapchainObject& swapchainObject):
|
VkClearManager& clearManager, VkTextureManager& textureManager, SwapchainObject& swapchainObject):
|
||||||
m_device(device), m_config(config), m_clearManager(clearManager), m_textureManager(textureManager),
|
m_device(device), m_physicalDevice(physicalDevice), m_allocator(allocator), m_config(config),
|
||||||
m_swapchainObject(swapchainObject) {
|
m_clearManager(clearManager), m_textureManager(textureManager), m_swapchainObject(swapchainObject) {
|
||||||
RenderPassEntry::s_device = m_device;
|
RenderPassEntry::s_device = m_device;
|
||||||
s_clearManager = &m_clearManager;
|
s_clearManager = &m_clearManager;
|
||||||
s_textureManager = &m_textureManager;
|
s_textureManager = &m_textureManager;
|
||||||
s_swapchainObject = &m_swapchainObject;
|
s_swapchainObject = &m_swapchainObject;
|
||||||
|
s_renderPassManager = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkRenderPassManager::~VkRenderPassManager() {}
|
VkRenderPassManager::~VkRenderPassManager() {}
|
||||||
@@ -131,13 +197,209 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
void VkRenderPassManager::Shutdown() {
|
void VkRenderPassManager::Shutdown() {
|
||||||
m_renderPasses.clear();
|
m_renderPasses.clear();
|
||||||
|
for (auto& [_, resource] : m_renderbufferResources) {
|
||||||
|
resource.Destroy(m_device, m_allocator);
|
||||||
|
}
|
||||||
|
m_renderbufferResources.clear();
|
||||||
|
m_pendingRenderbufferClears.clear();
|
||||||
RenderPassEntry::s_textureResourcesScratch.clear();
|
RenderPassEntry::s_textureResourcesScratch.clear();
|
||||||
s_activeRenderPass = {};
|
s_activeRenderPass = {};
|
||||||
s_hasActiveRenderPass = false;
|
s_hasActiveRenderPass = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VkRenderPassManager::CollectRenderbufferGarbage() {
|
||||||
|
Vector<MG_State::GLState::RenderbufferObject*> deadRenderbuffers;
|
||||||
|
deadRenderbuffers.reserve(m_renderbufferResources.size());
|
||||||
|
for (auto& [renderbuffer, resource] : m_renderbufferResources) {
|
||||||
|
const auto liveRenderbuffer = resource.renderbuffer.lock();
|
||||||
|
if (!liveRenderbuffer || liveRenderbuffer.get() != renderbuffer) {
|
||||||
|
deadRenderbuffers.emplace_back(renderbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto* renderbuffer : deadRenderbuffers) {
|
||||||
|
auto resourceIt = m_renderbufferResources.find(renderbuffer);
|
||||||
|
if (resourceIt != m_renderbufferResources.end()) {
|
||||||
|
resourceIt->second.Destroy(m_device, m_allocator);
|
||||||
|
m_renderbufferResources.erase(resourceIt);
|
||||||
|
}
|
||||||
|
m_pendingRenderbufferClears.erase(renderbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkRenderPassManager::RenderbufferResource* VkRenderPassManager::GetOrCreateRenderbufferResource(
|
||||||
|
const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer) {
|
||||||
|
if (!renderbuffer || !renderbuffer->IsAllocated()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectRenderbufferGarbage();
|
||||||
|
|
||||||
|
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
if (!TryResolveSampleCountFlagBits(renderbuffer->GetSamples(), sampleCount)) {
|
||||||
|
MGLOG_E("GetOrCreateRenderbufferResource: unsupported renderbuffer sample count %d for renderbuffer %u",
|
||||||
|
renderbuffer->GetSamples(),
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto internalFormat = renderbuffer->GetInternalFormat();
|
||||||
|
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
|
||||||
|
const VkImageAspectFlags aspect = ResolveImageAspectMaskForFormat(format);
|
||||||
|
if ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||||
|
MGLOG_E("GetOrCreateRenderbufferResource: color renderbuffer %u is not supported by DirectVulkan render passes yet",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& resource = m_renderbufferResources[renderbuffer.get()];
|
||||||
|
const Bool needsCreate =
|
||||||
|
resource.image == VK_NULL_HANDLE ||
|
||||||
|
resource.format != format ||
|
||||||
|
resource.extent.width != static_cast<Uint32>(renderbuffer->GetWidth()) ||
|
||||||
|
resource.extent.height != static_cast<Uint32>(renderbuffer->GetHeight()) ||
|
||||||
|
resource.sampleCount != sampleCount ||
|
||||||
|
resource.internalFormat != internalFormat ||
|
||||||
|
resource.samples != renderbuffer->GetSamples();
|
||||||
|
if (!needsCreate) {
|
||||||
|
resource.renderbuffer = renderbuffer;
|
||||||
|
return &resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Destroy(m_device, m_allocator);
|
||||||
|
resource.renderbuffer = renderbuffer;
|
||||||
|
|
||||||
|
VkImageCreateInfo imageInfo{};
|
||||||
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageInfo.extent.width = static_cast<Uint32>(renderbuffer->GetWidth());
|
||||||
|
imageInfo.extent.height = static_cast<Uint32>(renderbuffer->GetHeight());
|
||||||
|
imageInfo.extent.depth = 1;
|
||||||
|
imageInfo.mipLevels = 1;
|
||||||
|
imageInfo.arrayLayers = 1;
|
||||||
|
imageInfo.format = format;
|
||||||
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||||
|
imageInfo.samples = sampleCount;
|
||||||
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
VkImageFormatProperties imageFormatProperties{};
|
||||||
|
const VkResult imageFormatResult = vkGetPhysicalDeviceImageFormatProperties(
|
||||||
|
m_physicalDevice, format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags,
|
||||||
|
&imageFormatProperties);
|
||||||
|
if (imageFormatResult != VK_SUCCESS || (imageFormatProperties.sampleCounts & sampleCount) == 0) {
|
||||||
|
MGLOG_E("GetOrCreateRenderbufferResource: unsupported renderbuffer format=%d samples=%d for renderbuffer %u",
|
||||||
|
static_cast<Int>(format),
|
||||||
|
static_cast<Int>(sampleCount),
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo allocationInfo{};
|
||||||
|
allocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||||
|
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
|
||||||
|
"vmaCreateImage(renderbuffer)");
|
||||||
|
|
||||||
|
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.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
|
||||||
|
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
|
||||||
|
viewInfo.subresourceRange.aspectMask = aspect;
|
||||||
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||||
|
viewInfo.subresourceRange.levelCount = 1;
|
||||||
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||||
|
viewInfo.subresourceRange.layerCount = 1;
|
||||||
|
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &resource.view),
|
||||||
|
"vkCreateImageView(renderbuffer)");
|
||||||
|
|
||||||
|
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
resource.format = format;
|
||||||
|
resource.aspect = aspect;
|
||||||
|
resource.extent = {imageInfo.extent.width, imageInfo.extent.height};
|
||||||
|
resource.sampleCount = sampleCount;
|
||||||
|
resource.internalFormat = internalFormat;
|
||||||
|
resource.samples = renderbuffer->GetSamples();
|
||||||
|
return &resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool VkRenderPassManager::GetPendingRenderbufferClear(
|
||||||
|
MG_State::GLState::RenderbufferObject* renderbuffer, ClearAttachmentPayload& outPayload) const {
|
||||||
|
if (renderbuffer == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto it = m_pendingRenderbufferClears.find(renderbuffer);
|
||||||
|
if (it == m_pendingRenderbufferClears.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto liveRenderbuffer = it->second.renderbuffer.lock();
|
||||||
|
if (!liveRenderbuffer || liveRenderbuffer.get() != renderbuffer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outPayload = it->second.payload;
|
||||||
|
return outPayload.mask != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool VkRenderPassManager::HasPendingRenderbufferClear(
|
||||||
|
const MG_State::GLState::FramebufferAttachmentObject& attachment) const {
|
||||||
|
if (!attachment.IsRenderbuffer() || !attachment.GetRenderbuffer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClearAttachmentPayload payload{};
|
||||||
|
return GetPendingRenderbufferClear(attachment.GetRenderbuffer().get(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VkRenderPassManager::QueueRenderbufferClear(
|
||||||
|
const ClearAttachmentPayload& clearPayload,
|
||||||
|
const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||||
|
if (clearPayload.mask == 0 || !attachment.IsRenderbuffer() || !attachment.IsComplete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto renderbuffer = attachment.GetRenderbuffer();
|
||||||
|
if (!renderbuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& pending = m_pendingRenderbufferClears[renderbuffer.get()];
|
||||||
|
pending.renderbuffer = renderbuffer;
|
||||||
|
pending.payload.mask |= clearPayload.mask;
|
||||||
|
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||||
|
pending.payload.color = clearPayload.color;
|
||||||
|
}
|
||||||
|
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
|
pending.payload.depth = clearPayload.depth;
|
||||||
|
}
|
||||||
|
if ((clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) {
|
||||||
|
pending.payload.stencil = clearPayload.stencil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VkRenderPassManager::QueueRenderbufferClear(
|
||||||
|
GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
||||||
|
const MG_State::GLState::FramebufferObject& drawFbo) {
|
||||||
|
if ((mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
|
QueueRenderbufferClear(
|
||||||
|
ClearAttachmentPayload{.mask = GL_DEPTH_BUFFER_BIT, .depth = clearPayload.depth},
|
||||||
|
drawFbo.GetAttachment(FramebufferAttachmentType::Depth));
|
||||||
|
}
|
||||||
|
if ((mask & GL_STENCIL_BUFFER_BIT) != 0) {
|
||||||
|
QueueRenderbufferClear(
|
||||||
|
ClearAttachmentPayload{.mask = GL_STENCIL_BUFFER_BIT, .stencil = clearPayload.stencil},
|
||||||
|
drawFbo.GetAttachment(FramebufferAttachmentType::Stencil));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VkRenderPassManager::PopPendingRenderbufferClear(
|
||||||
|
MG_State::GLState::RenderbufferObject* renderbuffer) {
|
||||||
|
if (renderbuffer != nullptr) {
|
||||||
|
m_pendingRenderbufferClears.erase(renderbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
|
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
|
||||||
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) const {
|
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) {
|
||||||
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
||||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||||
if (isDefaultFbo) {
|
if (isDefaultFbo) {
|
||||||
@@ -224,6 +486,44 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, ¤tLayout, sizeof(currentLayout)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, ¤tLayout, sizeof(currentLayout)));
|
||||||
}
|
}
|
||||||
|
if (att.IsRenderbuffer() && att.GetRenderbuffer()) {
|
||||||
|
const auto& renderbuffer = att.GetRenderbuffer();
|
||||||
|
const auto internalFormat = renderbuffer->GetInternalFormat();
|
||||||
|
const Int width = renderbuffer->GetWidth();
|
||||||
|
const Int height = renderbuffer->GetHeight();
|
||||||
|
const Int samples = renderbuffer->GetSamples();
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &internalFormat, sizeof(internalFormat)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &width, sizeof(width)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &height, sizeof(height)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &samples, sizeof(samples)));
|
||||||
|
|
||||||
|
Uint64 imageIdentity = 0;
|
||||||
|
VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
auto* resource = GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (resource != nullptr) {
|
||||||
|
imageIdentity = reinterpret_cast<Uint64>(resource->image);
|
||||||
|
currentLayout = resource->layout;
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &resource->sampleCount, sizeof(resource->sampleCount)));
|
||||||
|
} else {
|
||||||
|
const VkSampleCountFlagBits fallbackSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &fallbackSampleCount, sizeof(fallbackSampleCount)));
|
||||||
|
}
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &imageIdentity, sizeof(imageIdentity)));
|
||||||
|
|
||||||
|
if (includePendingClear) {
|
||||||
|
const Bool hasClear = HasPendingRenderbufferClear(att);
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &hasClear, sizeof(hasClear)));
|
||||||
|
if (hasClear) {
|
||||||
|
ClearAttachmentPayload clearPayload{};
|
||||||
|
const Bool hasPayload = GetPendingRenderbufferClear(renderbuffer.get(), clearPayload);
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &hasPayload, sizeof(hasPayload)));
|
||||||
|
if (hasPayload) {
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &clearPayload.mask, sizeof(clearPayload.mask)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, ¤tLayout, sizeof(currentLayout)));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (Int i = 0; i < validDrawBufCount; ++i) {
|
for (Int i = 0; i < validDrawBufCount; ++i) {
|
||||||
@@ -250,17 +550,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (att.IsTexture() && m_clearManager.HasPendingClear(att)) {
|
if (att.IsTexture() && m_clearManager.HasPendingClear(att)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (HasPendingRenderbufferClear(att)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
|
const auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
|
||||||
if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt)) {
|
if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (HasPendingRenderbufferClear(depthAtt)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil);
|
const auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil);
|
||||||
if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt)) {
|
if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (HasPendingRenderbufferClear(stencilAtt)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -444,49 +753,74 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
depthAttachmentRef.attachment = VK_ATTACHMENT_UNUSED;
|
depthAttachmentRef.attachment = VK_ATTACHMENT_UNUSED;
|
||||||
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
VkTextureManager::TextureResource* depthTextureResource = nullptr;
|
VkTextureManager::TextureResource* depthTextureResource = nullptr;
|
||||||
|
RenderbufferResource* depthRenderbufferResource = nullptr;
|
||||||
const auto isUsableDepthStencilAttachment = [](const auto& attachment) {
|
const auto isUsableDepthStencilAttachment = [](const auto& attachment) {
|
||||||
return attachment.IsComplete() && attachment.IsTexture();
|
return attachment.IsComplete() && (attachment.IsTexture() || attachment.IsRenderbuffer());
|
||||||
|
};
|
||||||
|
const auto sameDepthStencilAttachmentObject = [](const auto& a, const auto& b) {
|
||||||
|
if (a.IsTexture() && b.IsTexture()) {
|
||||||
|
return a.GetTexture().get() == b.GetTexture().get() &&
|
||||||
|
a.GetTextureUploadTarget() == b.GetTextureUploadTarget() &&
|
||||||
|
a.GetTextureLevel() == b.GetTextureLevel();
|
||||||
|
}
|
||||||
|
if (a.IsRenderbuffer() && b.IsRenderbuffer()) {
|
||||||
|
return a.GetRenderbuffer().get() == b.GetRenderbuffer().get();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
const auto* selectedDepthStencilAttachment = isUsableDepthStencilAttachment(depthAtt) ? &depthAtt :
|
const auto* selectedDepthStencilAttachment = isUsableDepthStencilAttachment(depthAtt) ? &depthAtt :
|
||||||
(isUsableDepthStencilAttachment(stencilAtt) ? &stencilAtt : nullptr);
|
(isUsableDepthStencilAttachment(stencilAtt) ? &stencilAtt : nullptr);
|
||||||
const Bool hasDistinctDepthAndStencilAttachments =
|
const Bool hasDistinctDepthAndStencilAttachments =
|
||||||
isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) &&
|
isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) &&
|
||||||
(depthAtt.GetTexture().get() != stencilAtt.GetTexture().get() ||
|
!sameDepthStencilAttachmentObject(depthAtt, stencilAtt);
|
||||||
depthAtt.GetTextureUploadTarget() != stencilAtt.GetTextureUploadTarget() ||
|
|
||||||
depthAtt.GetTextureLevel() != stencilAtt.GetTextureLevel());
|
|
||||||
if (hasDistinctDepthAndStencilAttachments) {
|
if (hasDistinctDepthAndStencilAttachments) {
|
||||||
MGLOG_E("GetOrCreateRenderPass: separate depth/stencil attachments are not supported yet; using the depth attachment and ignoring the standalone stencil attachment for framebuffer %u",
|
MGLOG_E("GetOrCreateRenderPass: separate depth/stencil attachments are not supported yet; using the depth attachment and ignoring the standalone stencil attachment for framebuffer %u",
|
||||||
fbo.GetExternalIndex());
|
fbo.GetExternalIndex());
|
||||||
}
|
}
|
||||||
if (selectedDepthStencilAttachment != nullptr) {
|
if (selectedDepthStencilAttachment != nullptr) {
|
||||||
auto& texture = *selectedDepthStencilAttachment->GetTexture();
|
|
||||||
const Uint32 attachmentMipLevel =
|
|
||||||
static_cast<Uint32>(std::max(selectedDepthStencilAttachment->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(*selectedDepthStencilAttachment, clearPayload);
|
Bool hasClear = selectedDepthStencilAttachment->IsTexture()
|
||||||
|
? m_clearManager.GetPendingClear(*selectedDepthStencilAttachment, clearPayload)
|
||||||
|
: GetPendingRenderbufferClear(selectedDepthStencilAttachment->GetRenderbuffer().get(), clearPayload);
|
||||||
Bool clearDepth = hasClear && (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0;
|
Bool clearDepth = hasClear && (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0;
|
||||||
Bool clearStencil = hasClear && (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0;
|
Bool clearStencil = hasClear && (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0;
|
||||||
VkImageLayout trackedDepthLayout = isDefaultFbo ?
|
VkImageLayout trackedDepthLayout = isDefaultFbo ?
|
||||||
m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex) :
|
m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex) :
|
||||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
if (!isDefaultFbo) {
|
depthAttachmentDescription.flags = 0;
|
||||||
|
VkSampleCountFlagBits depthAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
Int depthAttachmentId = 0;
|
||||||
|
IntVec2 attachmentExtent = {0, 0};
|
||||||
|
if (isDefaultFbo) {
|
||||||
|
depthAttachmentDescription.format = m_swapchainObject.GetDepthStencilFormat();
|
||||||
|
depthAttachmentId = 0;
|
||||||
|
} else if (selectedDepthStencilAttachment->IsTexture()) {
|
||||||
|
auto& texture = *selectedDepthStencilAttachment->GetTexture();
|
||||||
depthTextureResource = m_textureManager.SyncTextureAndGetDescriptor(texture);
|
depthTextureResource = m_textureManager.SyncTextureAndGetDescriptor(texture);
|
||||||
MOBILEGL_ASSERT(depthTextureResource,
|
MOBILEGL_ASSERT(depthTextureResource,
|
||||||
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment");
|
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment");
|
||||||
trackedDepthLayout = depthTextureResource->layout;
|
trackedDepthLayout = depthTextureResource->layout;
|
||||||
}
|
|
||||||
depthAttachmentDescription.flags = 0;
|
|
||||||
depthAttachmentDescription.format = isDefaultFbo ?
|
|
||||||
m_swapchainObject.GetDepthStencilFormat() :
|
|
||||||
MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
|
|
||||||
VkSampleCountFlagBits depthAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
if (!isDefaultFbo) {
|
|
||||||
depthAttachmentDescription.format = depthTextureResource->format;
|
depthAttachmentDescription.format = depthTextureResource->format;
|
||||||
depthAttachmentSampleCount = depthTextureResource->sampleCount;
|
depthAttachmentSampleCount = depthTextureResource->sampleCount;
|
||||||
|
depthAttachmentId = static_cast<Int>(texture.GetExternalIndex());
|
||||||
|
attachmentExtent =
|
||||||
|
ResolveRenderPassFramebufferExtent(isDefaultFbo, selectedDepthStencilAttachment->GetSize(),
|
||||||
|
swapchainExtent);
|
||||||
|
} else {
|
||||||
|
const auto& renderbuffer = selectedDepthStencilAttachment->GetRenderbuffer();
|
||||||
|
depthRenderbufferResource = GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
MOBILEGL_ASSERT(depthRenderbufferResource,
|
||||||
|
"GetOrCreateRenderPass: GetOrCreateRenderbufferResource failed at depth attachment");
|
||||||
|
trackedDepthLayout = depthRenderbufferResource->layout;
|
||||||
|
depthAttachmentDescription.format = depthRenderbufferResource->format;
|
||||||
|
depthAttachmentSampleCount = depthRenderbufferResource->sampleCount;
|
||||||
|
depthAttachmentId = static_cast<Int>(renderbuffer->GetExternalIndex());
|
||||||
|
attachmentExtent = {static_cast<Int>(depthRenderbufferResource->extent.width),
|
||||||
|
static_cast<Int>(depthRenderbufferResource->extent.height)};
|
||||||
}
|
}
|
||||||
depthAttachmentDescription.samples = depthAttachmentSampleCount;
|
depthAttachmentDescription.samples = depthAttachmentSampleCount;
|
||||||
adoptRenderPassSampleCount(depthAttachmentSampleCount, "depth/stencil", texture.GetExternalIndex());
|
adoptRenderPassSampleCount(depthAttachmentSampleCount, "depth/stencil", depthAttachmentId);
|
||||||
const auto loadInfo =
|
const auto loadInfo =
|
||||||
ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil);
|
ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil);
|
||||||
depthAttachmentDescription.loadOp = loadInfo.depthLoadOp;
|
depthAttachmentDescription.loadOp = loadInfo.depthLoadOp;
|
||||||
@@ -496,15 +830,24 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
depthAttachmentDescription.initialLayout = loadInfo.initialLayout;
|
depthAttachmentDescription.initialLayout = loadInfo.initialLayout;
|
||||||
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) {
|
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) {
|
||||||
MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment textureId=%d starts with undefined layout "
|
MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment id=%d starts with undefined layout "
|
||||||
"and partial/no clear; using DONT_CARE for uncleared aspects",
|
"and partial/no clear; using DONT_CARE for uncleared aspects",
|
||||||
texture.GetExternalIndex());
|
depthAttachmentId);
|
||||||
}
|
}
|
||||||
if (hasClear) {
|
if (hasClear) {
|
||||||
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
|
if (selectedDepthStencilAttachment->IsTexture()) {
|
||||||
.attachmentIndex = depthAttachmentIndex,
|
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
|
||||||
.key = VkClearManager::MakePendingClearKey(*selectedDepthStencilAttachment)
|
.attachmentIndex = depthAttachmentIndex,
|
||||||
});
|
.key = VkClearManager::MakePendingClearKey(*selectedDepthStencilAttachment)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
|
||||||
|
.attachmentIndex = depthAttachmentIndex,
|
||||||
|
.renderbuffer = selectedDepthStencilAttachment->GetRenderbuffer().get(),
|
||||||
|
.hasInlinePayload = true,
|
||||||
|
.inlinePayload = clearPayload
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isDefaultFbo) {
|
if (isDefaultFbo) {
|
||||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||||
@@ -513,7 +856,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.finalLayout = depthAttachmentDescription.finalLayout,
|
.finalLayout = depthAttachmentDescription.finalLayout,
|
||||||
});
|
});
|
||||||
attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex));
|
attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex));
|
||||||
} else {
|
} else if (selectedDepthStencilAttachment->IsTexture()) {
|
||||||
|
auto& texture = *selectedDepthStencilAttachment->GetTexture();
|
||||||
|
const Uint32 attachmentMipLevel =
|
||||||
|
static_cast<Uint32>(std::max(selectedDepthStencilAttachment->GetTextureLevel(), 0));
|
||||||
MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
||||||
depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||||
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
|
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
|
||||||
@@ -538,9 +884,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
texture, attachmentMipLevel, baseArrayLayer, layerCount, attachmentViewType));
|
texture, attachmentMipLevel, baseArrayLayer, layerCount, attachmentViewType));
|
||||||
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
|
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
|
||||||
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment");
|
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment");
|
||||||
const IntVec2 attachmentExtent =
|
if (width == 0 || height == 0) {
|
||||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, selectedDepthStencilAttachment->GetSize(),
|
width = attachmentExtent.x();
|
||||||
swapchainExtent);
|
height = attachmentExtent.y();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto& renderbuffer = selectedDepthStencilAttachment->GetRenderbuffer();
|
||||||
|
MOBILEGL_ASSERT(depthRenderbufferResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
||||||
|
depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||||
|
"GetOrCreateRenderPass: depth renderbuffer %u has undefined tracked layout with LOAD_OP_LOAD",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
MOBILEGL_ASSERT(depthRenderbufferResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
||||||
|
depthAttachmentDescription.stencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||||
|
"GetOrCreateRenderPass: stencil renderbuffer %u has undefined tracked layout with LOAD_OP_LOAD",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||||
|
.target = TrackedAttachmentTarget::Renderbuffer,
|
||||||
|
.renderbuffer = renderbuffer,
|
||||||
|
.finalLayout = depthAttachmentDescription.finalLayout,
|
||||||
|
});
|
||||||
|
textureResources.emplace_back(nullptr);
|
||||||
|
attachmentViews.emplace_back(depthRenderbufferResource->view);
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
width = attachmentExtent.x();
|
width = attachmentExtent.x();
|
||||||
height = attachmentExtent.y();
|
height = attachmentExtent.y();
|
||||||
@@ -635,13 +999,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
clearValue.depthStencil = {1.0f, 0};
|
clearValue.depthStencil = {1.0f, 0};
|
||||||
}
|
}
|
||||||
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
|
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
|
||||||
if (pending.key.texture == nullptr || pending.attachmentIndex >= clearValues.size()) {
|
if (pending.attachmentIndex >= clearValues.size()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ClearAttachmentPayload clearPayload{};
|
ClearAttachmentPayload clearPayload{};
|
||||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||||
if (!s_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
if (pending.hasInlinePayload) {
|
||||||
continue;
|
clearPayload = pending.inlinePayload;
|
||||||
|
} else {
|
||||||
|
if (pending.key.texture == nullptr ||
|
||||||
|
!s_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||||
clearValues[pending.attachmentIndex].color = {
|
clearValues[pending.attachmentIndex].color = {
|
||||||
@@ -664,7 +1033,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
|
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
|
||||||
s_clearManager->PopPendingClear(pending.key);
|
if (pending.hasInlinePayload) {
|
||||||
|
if (s_renderPassManager != nullptr) {
|
||||||
|
s_renderPassManager->PopPendingRenderbufferClear(pending.renderbuffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_clearManager->PopPendingClear(pending.key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s_activeRenderPass.hash = renderPassEntry.hash;
|
s_activeRenderPass.hash = renderPassEntry.hash;
|
||||||
s_activeRenderPass.compatibilityHash = renderPassEntry.compatibilityHash;
|
s_activeRenderPass.compatibilityHash = renderPassEntry.compatibilityHash;
|
||||||
@@ -691,6 +1066,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
trackedAttachment.finalLayout);
|
trackedAttachment.finalLayout);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TrackedAttachmentTarget::Renderbuffer:
|
||||||
|
MOBILEGL_ASSERT(s_renderPassManager != nullptr, "EndRenderPass: render pass manager is null");
|
||||||
|
if (const auto renderbuffer = trackedAttachment.renderbuffer.lock()) {
|
||||||
|
auto resourceIt =
|
||||||
|
s_renderPassManager->m_renderbufferResources.find(renderbuffer.get());
|
||||||
|
if (resourceIt != s_renderPassManager->m_renderbufferResources.end()) {
|
||||||
|
resourceIt->second.layout = trackedAttachment.finalLayout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TrackedAttachmentTarget::SwapchainColor:
|
case TrackedAttachmentTarget::SwapchainColor:
|
||||||
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
||||||
s_swapchainObject->SetImageLayout(trackedAttachment.swapchainImageIndex, trackedAttachment.finalLayout);
|
s_swapchainObject->SetImageLayout(trackedAttachment.swapchainImageIndex, trackedAttachment.finalLayout);
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
|
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
|
||||||
|
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
enum class TrackedAttachmentTarget : Uint8 {
|
enum class TrackedAttachmentTarget : Uint8 {
|
||||||
Texture,
|
Texture,
|
||||||
|
Renderbuffer,
|
||||||
SwapchainColor,
|
SwapchainColor,
|
||||||
SwapchainDepthStencil
|
SwapchainDepthStencil
|
||||||
};
|
};
|
||||||
@@ -27,11 +29,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
struct PendingClearAttachmentInfo {
|
struct PendingClearAttachmentInfo {
|
||||||
Uint32 attachmentIndex = 0;
|
Uint32 attachmentIndex = 0;
|
||||||
PendingClearKey key{};
|
PendingClearKey key{};
|
||||||
|
MG_State::GLState::RenderbufferObject* renderbuffer = nullptr;
|
||||||
|
Bool hasInlinePayload = false;
|
||||||
|
ClearAttachmentPayload inlinePayload{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrackedAttachmentLayoutInfo {
|
struct TrackedAttachmentLayoutInfo {
|
||||||
TrackedAttachmentTarget target = TrackedAttachmentTarget::Texture;
|
TrackedAttachmentTarget target = TrackedAttachmentTarget::Texture;
|
||||||
WeakPtr<MG_State::GLState::ITextureObject> texture;
|
WeakPtr<MG_State::GLState::ITextureObject> texture;
|
||||||
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
Uint32 textureMipLevel = 0;
|
Uint32 textureMipLevel = 0;
|
||||||
Uint32 swapchainImageIndex = 0;
|
Uint32 swapchainImageIndex = 0;
|
||||||
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
@@ -143,8 +149,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
public:
|
public:
|
||||||
using HashType = Uint64;
|
using HashType = Uint64;
|
||||||
VkRenderPassManager(VkDevice device,
|
VkRenderPassManager(VkDevice device,
|
||||||
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
|
VkPhysicalDevice physicalDevice, VmaAllocator allocator, const VulkanRendererConfig& config,
|
||||||
SwapchainObject& swapchainObject);
|
VkClearManager& clearManager, VkTextureManager& textureManager, SwapchainObject& swapchainObject);
|
||||||
~VkRenderPassManager();
|
~VkRenderPassManager();
|
||||||
|
|
||||||
Bool Initialize();
|
Bool Initialize();
|
||||||
@@ -153,23 +159,64 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
HashType ComputeHash(
|
HashType ComputeHash(
|
||||||
const MG_State::GLState::FramebufferObject& fbo,
|
const MG_State::GLState::FramebufferObject& fbo,
|
||||||
Uint32 swapchainImageIndex,
|
Uint32 swapchainImageIndex,
|
||||||
Bool includePendingClear = true) const;
|
Bool includePendingClear = true);
|
||||||
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
|
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
|
||||||
|
void QueueRenderbufferClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
||||||
|
const MG_State::GLState::FramebufferObject& drawFbo);
|
||||||
|
void QueueRenderbufferClear(const ClearAttachmentPayload& clearPayload,
|
||||||
|
const MG_State::GLState::FramebufferAttachmentObject& attachment);
|
||||||
|
void PopPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer);
|
||||||
static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
|
static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
|
||||||
static Bool EndRenderPass(VkCommandBuffer commandBuffer);
|
static Bool EndRenderPass(VkCommandBuffer commandBuffer);
|
||||||
static ActiveRenderPassInfo* GetActiveRenderPass();
|
static ActiveRenderPassInfo* GetActiveRenderPass();
|
||||||
private:
|
private:
|
||||||
VkDevice m_device = VK_NULL_HANDLE;
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
|
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||||
|
VmaAllocator m_allocator = nullptr;
|
||||||
const VulkanRendererConfig& m_config;
|
const VulkanRendererConfig& m_config;
|
||||||
VkClearManager& m_clearManager;
|
VkClearManager& m_clearManager;
|
||||||
VkTextureManager& m_textureManager;
|
VkTextureManager& m_textureManager;
|
||||||
SwapchainObject& m_swapchainObject;
|
SwapchainObject& m_swapchainObject;
|
||||||
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
|
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
|
||||||
|
|
||||||
|
struct RenderbufferResource {
|
||||||
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
VmaAllocation allocation = nullptr;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE;
|
||||||
|
VkExtent2D extent = {0, 0};
|
||||||
|
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
|
||||||
|
Int samples = 0;
|
||||||
|
|
||||||
|
void Destroy(VkDevice device, VmaAllocator allocator);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingRenderbufferClear {
|
||||||
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
|
ClearAttachmentPayload payload{};
|
||||||
|
};
|
||||||
|
|
||||||
|
UnorderedMap<MG_State::GLState::RenderbufferObject*, RenderbufferResource> m_renderbufferResources;
|
||||||
|
UnorderedMap<MG_State::GLState::RenderbufferObject*, PendingRenderbufferClear> m_pendingRenderbufferClears;
|
||||||
|
|
||||||
|
RenderbufferResource* GetOrCreateRenderbufferResource(
|
||||||
|
const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer);
|
||||||
|
Bool GetPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer,
|
||||||
|
ClearAttachmentPayload& outPayload) const;
|
||||||
|
Bool HasPendingRenderbufferClear(
|
||||||
|
const MG_State::GLState::FramebufferAttachmentObject& attachment) const;
|
||||||
|
void CollectRenderbufferGarbage();
|
||||||
|
|
||||||
static inline XXH64_state_t* m_hashState = XXH64_createState();
|
static inline XXH64_state_t* m_hashState = XXH64_createState();
|
||||||
static inline ActiveRenderPassInfo s_activeRenderPass{};
|
static inline ActiveRenderPassInfo s_activeRenderPass{};
|
||||||
static inline Bool s_hasActiveRenderPass = false;
|
static inline Bool s_hasActiveRenderPass = false;
|
||||||
static inline VkClearManager* s_clearManager = nullptr;
|
static inline VkClearManager* s_clearManager = nullptr;
|
||||||
static inline VkTextureManager* s_textureManager = nullptr;
|
static inline VkTextureManager* s_textureManager = nullptr;
|
||||||
static inline SwapchainObject* s_swapchainObject = nullptr;
|
static inline SwapchainObject* s_swapchainObject = nullptr;
|
||||||
|
static inline VkRenderPassManager* s_renderPassManager = nullptr;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -897,23 +897,47 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
depthAttachment.GetTextureLevel() != stencilAttachment.GetTextureLevel();
|
depthAttachment.GetTextureLevel() != stencilAttachment.GetTextureLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bool HasCompleteRenderbufferAttachment(const MG_State::GLState::FramebufferObject& framebufferObject) {
|
static Bool IsColorAttachment(FramebufferAttachmentType attachmentType) {
|
||||||
|
return attachmentType >= FramebufferAttachmentType::Color0 &&
|
||||||
|
attachmentType <= FramebufferAttachmentType::Color31;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool HasUnsupportedCompleteRenderbufferAttachment(
|
||||||
|
const MG_State::GLState::FramebufferObject& framebufferObject) {
|
||||||
if (framebufferObject.GetExternalIndex() == 0) {
|
if (framebufferObject.GetExternalIndex() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& attachment : framebufferObject.GetAllAttachmentObjects()) {
|
const auto& attachments = framebufferObject.GetAllAttachmentObjects();
|
||||||
|
for (SizeT i = 0; i < attachments.size(); ++i) {
|
||||||
|
const auto attachmentType = static_cast<FramebufferAttachmentType>(i);
|
||||||
|
const auto& attachment = attachments[i];
|
||||||
if (attachment.IsRenderbuffer() && attachment.IsComplete()) {
|
if (attachment.IsRenderbuffer() && attachment.IsComplete()) {
|
||||||
return true;
|
if (IsColorAttachment(attachmentType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& depthAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Depth);
|
||||||
|
const auto& stencilAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Stencil);
|
||||||
|
if (!depthAttachment.IsComplete() || !stencilAttachment.IsComplete()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (depthAttachment.IsRenderbuffer() && stencilAttachment.IsRenderbuffer()) {
|
||||||
|
return depthAttachment.GetRenderbuffer().get() != stencilAttachment.GetRenderbuffer().get();
|
||||||
|
}
|
||||||
|
if ((depthAttachment.IsRenderbuffer() || stencilAttachment.IsRenderbuffer()) &&
|
||||||
|
(depthAttachment.IsTexture() || stencilAttachment.IsTexture())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bool IsUnsupportedFramebufferForDirectVulkan(
|
static Bool IsUnsupportedFramebufferForDirectVulkan(
|
||||||
const MG_State::GLState::FramebufferObject& framebufferObject) {
|
const MG_State::GLState::FramebufferObject& framebufferObject) {
|
||||||
return HasDistinctCompleteDepthStencilTextureAttachments(framebufferObject) ||
|
return HasDistinctCompleteDepthStencilTextureAttachments(framebufferObject) ||
|
||||||
HasCompleteRenderbufferAttachment(framebufferObject);
|
HasUnsupportedCompleteRenderbufferAttachment(framebufferObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RecordUnsupportedFramebufferError(const char* func) {
|
static void RecordUnsupportedFramebufferError(const char* func) {
|
||||||
@@ -1842,7 +1866,8 @@ void main() {
|
|||||||
succeeded = m_clearManager->Initialize();
|
succeeded = m_clearManager->Initialize();
|
||||||
MOBILEGL_ASSERT(succeeded, "VkClearManager initialization failed.");
|
MOBILEGL_ASSERT(succeeded, "VkClearManager initialization failed.");
|
||||||
m_renderPassManager =
|
m_renderPassManager =
|
||||||
MakeUnique<VkRenderPassManager>(m_device, m_config, *m_clearManager, *m_textureManager, m_swapchainObject);
|
MakeUnique<VkRenderPassManager>(m_device, m_physicalDevice.handle, m_allocator, m_config, *m_clearManager,
|
||||||
|
*m_textureManager, m_swapchainObject);
|
||||||
MOBILEGL_ASSERT(m_renderPassManager != nullptr, "VkRenderPassManager creation failed.");
|
MOBILEGL_ASSERT(m_renderPassManager != nullptr, "VkRenderPassManager creation failed.");
|
||||||
succeeded = m_renderPassManager->Initialize();
|
succeeded = m_renderPassManager->Initialize();
|
||||||
MOBILEGL_ASSERT(succeeded, "VkRenderPassManager initialization failed.");
|
MOBILEGL_ASSERT(succeeded, "VkRenderPassManager initialization failed.");
|
||||||
@@ -3438,6 +3463,7 @@ void main() {
|
|||||||
.stencil = MG_State::pGLContext->GetClearStencil()
|
.stencil = MG_State::pGLContext->GetClearStencil()
|
||||||
};
|
};
|
||||||
m_clearManager->QueueClear(mask, payload, *fbo);
|
m_clearManager->QueueClear(mask, payload, *fbo);
|
||||||
|
m_renderPassManager->QueueRenderbufferClear(mask, payload, *fbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::QueueClearBufferPayloadForFramebuffer(
|
void VulkanRenderer::QueueClearBufferPayloadForFramebuffer(
|
||||||
@@ -3454,7 +3480,11 @@ void main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto& attachment = framebuffer.GetAttachment(attachmentType);
|
const auto& attachment = framebuffer.GetAttachment(attachmentType);
|
||||||
if (!attachment.IsTexture() || attachment.IsRenderbuffer()) {
|
if (attachment.IsRenderbuffer()) {
|
||||||
|
m_renderPassManager->QueueRenderbufferClear(clearPayload, attachment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!attachment.IsTexture()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_clearManager->QueueClear(clearPayload, attachment);
|
m_clearManager->QueueClear(clearPayload, attachment);
|
||||||
@@ -6097,14 +6127,18 @@ void main() {
|
|||||||
clearRect.layerCount = 1;
|
clearRect.layerCount = 1;
|
||||||
|
|
||||||
for (const auto& pending : compatibleRenderPassEntry.pendingClearAttachments) {
|
for (const auto& pending : compatibleRenderPassEntry.pendingClearAttachments) {
|
||||||
if (pending.key.texture == nullptr) {
|
if (!pending.hasInlinePayload && pending.key.texture == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearAttachmentPayload clearPayload{};
|
ClearAttachmentPayload clearPayload{};
|
||||||
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
SharedPtr<MG_State::GLState::ITextureObject> liveTexture;
|
||||||
if (!m_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
if (pending.hasInlinePayload) {
|
||||||
continue;
|
clearPayload = pending.inlinePayload;
|
||||||
|
} else {
|
||||||
|
if (!m_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkClearAttachment clearAttachment{};
|
VkClearAttachment clearAttachment{};
|
||||||
@@ -6133,7 +6167,11 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
|
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
|
||||||
m_clearManager->PopPendingClear(pending.key);
|
if (pending.hasInlinePayload) {
|
||||||
|
m_renderPassManager->PopPendingRenderbufferClear(pending.renderbuffer);
|
||||||
|
} else {
|
||||||
|
m_clearManager->PopPendingClear(pending.key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ namespace {
|
|||||||
private:
|
private:
|
||||||
UniquePtr<MobileGL::MG_Backend::BackendObject> m_previous;
|
UniquePtr<MobileGL::MG_Backend::BackendObject> m_previous;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Uint8* GetBoundTexture2DLevelBytes(GLuint texture, Uint level = 0) {
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
return static_cast<const Uint8*>(mipmapObject->MapMipmapData(TextureUploadTarget::Texture2D, level));
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_F(TextureTest, CreateTexturesCreatesObjectsWithoutBinding) {
|
TEST_F(TextureTest, CreateTexturesCreatesObjectsWithoutBinding) {
|
||||||
@@ -187,6 +193,133 @@ TEST_F(TextureTest, BoundTexSubImage2DUsesCompactRowsAfterUnpackProcessing) {
|
|||||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexSubImage2DUnpacksPackedBgra8888ToRgba8) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, nullptr);
|
||||||
|
|
||||||
|
const Uint8 pixels[] = {
|
||||||
|
10, 20, 30, 40,
|
||||||
|
50, 60, 70, 80,
|
||||||
|
};
|
||||||
|
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
|
||||||
|
|
||||||
|
const auto* stored = GetBoundTexture2DLevelBytes(texture);
|
||||||
|
const Uint8 expected[] = {
|
||||||
|
20, 30, 40, 10,
|
||||||
|
60, 70, 80, 50,
|
||||||
|
};
|
||||||
|
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||||
|
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexImage2DUnpacksPackedBgra8888ToRgba8WithPixelStoreSkips) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
const Uint8 pixels[] = {
|
||||||
|
1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 10, 11, 12,
|
||||||
|
13, 14, 15, 16,
|
||||||
|
17, 18, 19, 20,
|
||||||
|
10, 20, 30, 40,
|
||||||
|
50, 60, 70, 80,
|
||||||
|
21, 22, 23, 24,
|
||||||
|
25, 26, 27, 28,
|
||||||
|
90, 100, 110, 120,
|
||||||
|
130, 140, 150, 160,
|
||||||
|
29, 30, 31, 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ROW_LENGTH, 4);
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_PIXELS, 1);
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_ROWS, 1);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||||
|
|
||||||
|
const auto* stored = GetBoundTexture2DLevelBytes(texture);
|
||||||
|
const Uint8 expected[] = {
|
||||||
|
20, 30, 40, 10,
|
||||||
|
60, 70, 80, 50,
|
||||||
|
100, 110, 120, 90,
|
||||||
|
140, 150, 160, 130,
|
||||||
|
};
|
||||||
|
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||||
|
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexSubImage2DUnpacksPackedBgra8888RevToRgba8) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
||||||
|
|
||||||
|
const Uint8 pixels[] = {
|
||||||
|
10, 20, 30, 40,
|
||||||
|
50, 60, 70, 80,
|
||||||
|
};
|
||||||
|
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||||
|
|
||||||
|
const auto* stored = GetBoundTexture2DLevelBytes(texture);
|
||||||
|
const Uint8 expected[] = {
|
||||||
|
30, 20, 10, 40,
|
||||||
|
70, 60, 50, 80,
|
||||||
|
};
|
||||||
|
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||||
|
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexSubImage2DUnpacksPackedRgba8888ToRgba8) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, nullptr);
|
||||||
|
|
||||||
|
const Uint8 pixels[] = {
|
||||||
|
10, 20, 30, 40,
|
||||||
|
50, 60, 70, 80,
|
||||||
|
};
|
||||||
|
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels);
|
||||||
|
|
||||||
|
const auto* stored = GetBoundTexture2DLevelBytes(texture);
|
||||||
|
const Uint8 expected[] = {
|
||||||
|
40, 30, 20, 10,
|
||||||
|
80, 70, 60, 50,
|
||||||
|
};
|
||||||
|
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||||
|
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexSubImage2DKeepsPackedRgba8888RevAsRgba8) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
||||||
|
|
||||||
|
const Uint8 pixels[] = {
|
||||||
|
10, 20, 30, 40,
|
||||||
|
50, 60, 70, 80,
|
||||||
|
};
|
||||||
|
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||||
|
|
||||||
|
const auto* stored = GetBoundTexture2DLevelBytes(texture);
|
||||||
|
EXPECT_EQ(std::memcmp(stored, pixels, sizeof(pixels)), 0);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TextureTest, BoundTexStorage2DAllocatesRedTextureForSubImageUpdates) {
|
TEST_F(TextureTest, BoundTexStorage2DAllocatesRedTextureForSubImageUpdates) {
|
||||||
GLuint texture = 0;
|
GLuint texture = 0;
|
||||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
|||||||
@@ -70,6 +70,31 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Bool GetRgba8ByteSwizzleForUnpack(TextureInputFormat inputFormat, TexturePixelDataType inputDataType,
|
||||||
|
Vector<TextureSwizzleParam>& swizzle) {
|
||||||
|
if (inputFormat == TextureInputFormat::RGBA) {
|
||||||
|
if (inputDataType == TexturePixelDataType::UnsignedInt8888) {
|
||||||
|
swizzle = {TextureSwizzleParam::Alpha, TextureSwizzleParam::Blue, TextureSwizzleParam::Green,
|
||||||
|
TextureSwizzleParam::Red};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputFormat == TextureInputFormat::BGRA) {
|
||||||
|
if (inputDataType == TexturePixelDataType::UnsignedInt8888) {
|
||||||
|
swizzle = {TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha,
|
||||||
|
TextureSwizzleParam::Red};
|
||||||
|
} else {
|
||||||
|
swizzle = {TextureSwizzleParam::Blue, TextureSwizzleParam::Green, TextureSwizzleParam::Red,
|
||||||
|
TextureSwizzleParam::Alpha};
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// assume 8 bit per channel
|
// assume 8 bit per channel
|
||||||
// swizzle.size() == channel count
|
// swizzle.size() == channel count
|
||||||
void ProcessColorSwizzle(void* data, SizeT pixelCount, const Vector<TextureSwizzleParam>& swizzle) {
|
void ProcessColorSwizzle(void* data, SizeT pixelCount, const Vector<TextureSwizzleParam>& swizzle) {
|
||||||
@@ -132,6 +157,10 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
|
|
||||||
Bool isByteType =
|
Bool isByteType =
|
||||||
(inputDataType == TexturePixelDataType::UnsignedByte || inputDataType == TexturePixelDataType::Byte);
|
(inputDataType == TexturePixelDataType::UnsignedByte || inputDataType == TexturePixelDataType::Byte);
|
||||||
|
Vector<TextureSwizzleParam> colorSwizzle;
|
||||||
|
const Bool needColorSwizzle =
|
||||||
|
targetInternalFormat == TextureInternalFormat::RGBA8 &&
|
||||||
|
GetRgba8ByteSwizzleForUnpack(textureInputFormat, inputDataType, colorSwizzle);
|
||||||
for (Int z = 0; z < copyDepth; ++z) {
|
for (Int z = 0; z < copyDepth; ++z) {
|
||||||
const Uint8* layerSrc = src;
|
const Uint8* layerSrc = src;
|
||||||
Uint8* layerDst = dst;
|
Uint8* layerDst = dst;
|
||||||
@@ -149,13 +178,10 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
ProcessLSBFirst(layerDst, static_cast<SizeT>(copyWidth), 1);
|
ProcessLSBFirst(layerDst, static_cast<SizeT>(copyWidth), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textureInputFormat == TextureInputFormat::BGRA &&
|
if (needColorSwizzle) {
|
||||||
targetInternalFormat == TextureInternalFormat::RGBA8) {
|
MGLOG_D("%s: Swizzle RGBA8 unpack", __func__);
|
||||||
MGLOG_D("%s: Swizzle (BGRA)", __func__);
|
|
||||||
// MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst));
|
// MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst));
|
||||||
ProcessColorSwizzle(layerDst, static_cast<SizeT>(copyWidth),
|
ProcessColorSwizzle(layerDst, static_cast<SizeT>(copyWidth), colorSwizzle);
|
||||||
{TextureSwizzleParam::Blue, TextureSwizzleParam::Green,
|
|
||||||
TextureSwizzleParam::Red, TextureSwizzleParam::Alpha});
|
|
||||||
// MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst));
|
// MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst));
|
||||||
}
|
}
|
||||||
// else
|
// else
|
||||||
|
|||||||
Reference in New Issue
Block a user