mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (MG_Backend/DirectVulkan): support Voxy rendering
Implemented: - Advertise Voxy-required DirectVulkan extensions without raising the reported OpenGL version. - Add DirectVulkan compute, indirect draw count, DSA, readback, and buffer state paths needed by Voxy. Fixed: - Enable Vulkan shaderInt64 and drawIndirectFirstInstance so Voxy baseInstance-driven LOD draws address the correct section data. - Fix DirectVulkan synchronization, framebuffer, texture readback, and shader interface handling used by Voxy and Minecraft screenshots. Tests: - Add MG_Test coverage for DirectVulkan extension advertising, DSA buffer/texture/framebuffer/vertex-array behavior, persistent mapped readback, and shader/program paths.
This commit is contained in:
@@ -178,9 +178,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
packet.signalSemaphore = m_swapchainImageRenderFinishedSemaphores[swapchainImageIndex];
|
||||
packet.commandBuffer = frame.commandBuffer;
|
||||
|
||||
packet.submitInfo.waitSemaphoreCount = 1;
|
||||
packet.submitInfo.pWaitSemaphores = &packet.waitSemaphore;
|
||||
packet.submitInfo.pWaitDstStageMask = &packet.waitDstStageMask;
|
||||
packet.submitInfo.waitSemaphoreCount = frame.imageAvailableSemaphoreConsumed ? 0U : 1U;
|
||||
packet.submitInfo.pWaitSemaphores = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitSemaphore;
|
||||
packet.submitInfo.pWaitDstStageMask = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitDstStageMask;
|
||||
packet.submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U;
|
||||
packet.submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &packet.commandBuffer : nullptr;
|
||||
packet.submitInfo.signalSemaphoreCount = 1;
|
||||
@@ -217,6 +217,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return result;
|
||||
}
|
||||
|
||||
frame.imageAvailableSemaphoreConsumed = false;
|
||||
return vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence,
|
||||
&outImageIndex);
|
||||
}
|
||||
@@ -260,6 +261,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
frame.hasCommandBufferRecorded = false;
|
||||
frame.isCommandRecording = false;
|
||||
frame.imageAvailableSemaphoreConsumed = false;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -277,5 +279,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
frame.imageAvailableSemaphore = VK_NULL_HANDLE;
|
||||
frame.isCommandRecording = false;
|
||||
frame.hasCommandBufferRecorded = false;
|
||||
frame.imageAvailableSemaphoreConsumed = false;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkFence imageInFlightFence = VK_NULL_HANDLE;
|
||||
Bool isCommandRecording = false;
|
||||
Bool hasCommandBufferRecorded = false;
|
||||
Bool imageAvailableSemaphoreConsumed = false;
|
||||
};
|
||||
|
||||
VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount);
|
||||
|
||||
@@ -994,16 +994,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
String NormalizeDescriptorName(const SpvReflectDescriptorBinding& binding,
|
||||
ProgramFactory::DescriptorBindingKind kind) {
|
||||
const char* rawName = binding.name;
|
||||
if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic &&
|
||||
if ((kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic ||
|
||||
kind == ProgramFactory::DescriptorBindingKind::StorageBuffer) &&
|
||||
binding.type_description != nullptr && binding.type_description->type_name != nullptr) {
|
||||
rawName = binding.type_description->type_name;
|
||||
}
|
||||
|
||||
MOBILEGL_ASSERT(rawName != nullptr && rawName[0] != '\0',
|
||||
"ProgramFactory: descriptor has empty name (spirvId=%u type=%d)", binding.spirv_id,
|
||||
static_cast<Int>(binding.descriptor_type));
|
||||
|
||||
String name = rawName;
|
||||
String name = (rawName != nullptr) ? rawName : "";
|
||||
if (name.empty()) {
|
||||
name = std::format("__mg_unnamed_descriptor_set{}_binding{}_id{}", binding.set, binding.binding,
|
||||
binding.spirv_id);
|
||||
MGLOG_W("ProgramFactory: descriptor has empty name; using generated name '%s' (type=%d)",
|
||||
name.c_str(), static_cast<Int>(binding.descriptor_type));
|
||||
}
|
||||
if (kind == ProgramFactory::DescriptorBindingKind::CombinedImageSampler ||
|
||||
kind == ProgramFactory::DescriptorBindingKind::UniformTexelBuffer ||
|
||||
kind == ProgramFactory::DescriptorBindingKind::StorageImage) {
|
||||
|
||||
@@ -202,7 +202,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const ProgramFactory::VkProgramObject& programObj,
|
||||
Uint32 binding, VkDescriptorImageInfo& outImageInfo) const {
|
||||
(void)commandBuffer;
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null");
|
||||
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null");
|
||||
MOBILEGL_ASSERT(binding < programObj.samplerNameByBinding.size(),
|
||||
@@ -254,23 +253,31 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Int attachmentLevel = 0;
|
||||
if (drawFbo &&
|
||||
FindFramebufferAttachmentForTexture(*drawFbo, *texture, attachmentType, attachmentLevel)) {
|
||||
MOBILEGL_ASSERT(false,
|
||||
"ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
|
||||
"for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, "
|
||||
"trackedLayout=%d)",
|
||||
texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(),
|
||||
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
|
||||
attachmentLevel, static_cast<Int>(resource->layout));
|
||||
MGLOG_W("ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
|
||||
"for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, "
|
||||
"trackedLayout=%d)",
|
||||
texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(),
|
||||
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
|
||||
attachmentLevel, static_cast<Int>(resource->layout));
|
||||
}
|
||||
|
||||
MOBILEGL_ASSERT(false,
|
||||
const Bool readyForSampling = m_textureManager->TransitionTextureForSampling(commandBuffer, *texture);
|
||||
if (!readyForSampling) {
|
||||
MGLOG_E("ResolveSamplerDescriptor: failed to transition textureId=%d for sampler binding=%u",
|
||||
texture->GetExternalIndex(), binding);
|
||||
return false;
|
||||
}
|
||||
resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
||||
MOBILEGL_ASSERT(resource != nullptr,
|
||||
"ResolveSamplerDescriptor: failed to resync textureId=%d after sampling transition",
|
||||
texture->GetExternalIndex());
|
||||
MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout),
|
||||
"ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u",
|
||||
static_cast<Int>(resource->layout), texture->GetExternalIndex(), binding);
|
||||
return false;
|
||||
}
|
||||
outImageInfo = {
|
||||
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture),
|
||||
.imageView = resource->fullView,
|
||||
.imageView = resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : resource->fullView,
|
||||
.imageLayout = resource->layout,
|
||||
};
|
||||
if (ShouldDumpDescriptorStats()) {
|
||||
@@ -316,7 +323,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
*samplerBindingOverride.texture),
|
||||
.imageView = samplerBindingOverride.imageView != VK_NULL_HANDLE ?
|
||||
samplerBindingOverride.imageView :
|
||||
resource->fullView,
|
||||
(resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : resource->fullView),
|
||||
.imageLayout = resource->layout,
|
||||
};
|
||||
return outImageInfo.sampler != VK_NULL_HANDLE;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
.allocator = m_initInfo.allocator,
|
||||
.frameCount = m_initInfo.frameCount,
|
||||
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
||||
.memoryUsage = m_initInfo.transientMemoryUsage,
|
||||
.allocationFlags = m_initInfo.transientAllocationFlags,
|
||||
.minBufferSize = m_initInfo.minUploadBytes,
|
||||
@@ -201,7 +201,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
case BufferKind::TextureBuffer:
|
||||
return VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
case BufferKind::ShaderStorage:
|
||||
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
||||
case BufferKind::Indirect:
|
||||
return VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uniform,
|
||||
TextureBuffer,
|
||||
ShaderStorage,
|
||||
Indirect,
|
||||
};
|
||||
|
||||
struct VkBufferManagerInitInfo {
|
||||
|
||||
@@ -61,6 +61,23 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return texture;
|
||||
}
|
||||
|
||||
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(
|
||||
VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil) {
|
||||
DepthStencilAttachmentLoadInfo info{};
|
||||
info.depthLoadOp = clearDepth
|
||||
? VK_ATTACHMENT_LOAD_OP_CLEAR
|
||||
: (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
|
||||
: VK_ATTACHMENT_LOAD_OP_LOAD);
|
||||
info.stencilLoadOp = clearStencil
|
||||
? VK_ATTACHMENT_LOAD_OP_CLEAR
|
||||
: (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
|
||||
: VK_ATTACHMENT_LOAD_OP_LOAD);
|
||||
info.initialLayout =
|
||||
(trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED || (clearDepth && clearStencil)) ? VK_IMAGE_LAYOUT_UNDEFINED
|
||||
: trackedLayout;
|
||||
return info;
|
||||
}
|
||||
|
||||
VkRenderPassManager::VkRenderPassManager(VkDevice device,
|
||||
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
|
||||
SwapchainObject& swapchainObject):
|
||||
@@ -372,27 +389,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
depthAttachmentDescription.format = depthTextureResource->format;
|
||||
}
|
||||
depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
depthAttachmentDescription.loadOp = clearDepth ?
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
const auto loadInfo =
|
||||
ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil);
|
||||
depthAttachmentDescription.loadOp = loadInfo.depthLoadOp;
|
||||
depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
depthAttachmentDescription.stencilLoadOp = clearStencil ?
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
depthAttachmentDescription.stencilLoadOp = loadInfo.stencilLoadOp;
|
||||
depthAttachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
if (!clearDepth) {
|
||||
depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
}
|
||||
if (!clearStencil) {
|
||||
depthAttachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
}
|
||||
depthAttachmentDescription.initialLayout = loadInfo.initialLayout;
|
||||
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) {
|
||||
MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment textureId=%d starts with undefined layout "
|
||||
"and partial/no clear; using DONT_CARE for uncleared aspects",
|
||||
texture.GetExternalIndex());
|
||||
}
|
||||
depthAttachmentDescription.initialLayout =
|
||||
(clearDepth && clearStencil) || trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED ?
|
||||
VK_IMAGE_LAYOUT_UNDEFINED :
|
||||
trackedDepthLayout;
|
||||
if (hasClear) {
|
||||
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
|
||||
.attachmentIndex = depthAttachmentIndex,
|
||||
@@ -407,9 +416,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
});
|
||||
attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex));
|
||||
} else {
|
||||
MOBILEGL_ASSERT(clearDepth || clearStencil || depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
||||
depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
|
||||
texture.GetExternalIndex());
|
||||
MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
|
||||
depthAttachmentDescription.stencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
"GetOrCreateRenderPass: stencil attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
|
||||
texture.GetExternalIndex());
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::Texture,
|
||||
.texture = &texture,
|
||||
|
||||
@@ -37,6 +37,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
};
|
||||
|
||||
struct DepthStencilAttachmentLoadInfo {
|
||||
VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
VkAttachmentLoadOp stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
};
|
||||
|
||||
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(
|
||||
VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil);
|
||||
|
||||
struct RenderPassEntry {
|
||||
static inline VkDevice s_device;
|
||||
static inline Vector<VkTextureManager::TextureResource*> s_textureResourcesScratch;
|
||||
|
||||
@@ -621,7 +621,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat());
|
||||
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo);
|
||||
perMipSampledView = CreateImageView(resource->image, resource->format, resource->aspect, resource->viewType,
|
||||
const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource->aspect);
|
||||
perMipSampledView = CreateImageView(resource->image, resource->format, sampledAspect, resource->viewType,
|
||||
mipLevel, 1, resource->arrayLayers, &sampledComponents);
|
||||
if (perMipSampledView == VK_NULL_HANDLE) {
|
||||
MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(),
|
||||
@@ -1014,6 +1015,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void VkTextureManager::DeferResourceRelease(TextureResource&& resource) {
|
||||
if (resource.image == VK_NULL_HANDLE && resource.fullView == VK_NULL_HANDLE &&
|
||||
resource.sampledView == VK_NULL_HANDLE &&
|
||||
resource.perMipViews.empty() && resource.perMipSampledViews.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1088,6 +1090,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
const Bool needsRecreate =
|
||||
resource.fullView == VK_NULL_HANDLE ||
|
||||
resource.sampledView == VK_NULL_HANDLE ||
|
||||
resource.sampledBaseMipLevel != baseMipLevel ||
|
||||
resource.sampledLevelCount != levelCount ||
|
||||
resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion();
|
||||
@@ -1099,6 +1102,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
DeferViewRelease(resource.fullView);
|
||||
resource.fullView = VK_NULL_HANDLE;
|
||||
}
|
||||
if (resource.sampledView != VK_NULL_HANDLE) {
|
||||
DeferViewRelease(resource.sampledView);
|
||||
resource.sampledView = VK_NULL_HANDLE;
|
||||
}
|
||||
for (auto& sampledView : resource.perMipSampledViews) {
|
||||
if (sampledView != VK_NULL_HANDLE) {
|
||||
DeferViewRelease(sampledView);
|
||||
@@ -1109,10 +1116,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat());
|
||||
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo);
|
||||
resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, resource.viewType,
|
||||
baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents);
|
||||
baseMipLevel, levelCount, resource.arrayLayers);
|
||||
if (resource.fullView == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource.aspect);
|
||||
resource.sampledView = CreateImageView(resource.image, resource.format, sampledAspect, resource.viewType,
|
||||
baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents);
|
||||
if (resource.sampledView == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
resource.sampledBaseMipLevel = baseMipLevel;
|
||||
resource.sampledLevelCount = levelCount;
|
||||
@@ -1441,4 +1454,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
VkImageAspectFlags VkTextureManager::ResolveSampledImageViewAspectMask(VkImageAspectFlags imageAspect) {
|
||||
if ((imageAspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
if ((imageAspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
|
||||
return VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
if ((imageAspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) {
|
||||
return VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
return imageAspect;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation = nullptr;
|
||||
VkImageView fullView = VK_NULL_HANDLE;
|
||||
VkImageView sampledView = VK_NULL_HANDLE;
|
||||
Vector<VkImageView> perMipViews;
|
||||
Vector<VkImageView> perMipSampledViews;
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
@@ -53,6 +54,7 @@ public:
|
||||
std::swap(this->image, that.image);
|
||||
std::swap(this->allocation, that.allocation);
|
||||
std::swap(this->fullView, that.fullView);
|
||||
std::swap(this->sampledView, that.sampledView);
|
||||
std::swap(this->perMipViews, that.perMipViews);
|
||||
std::swap(this->perMipSampledViews, that.perMipSampledViews);
|
||||
std::swap(this->layout, that.layout);
|
||||
@@ -72,6 +74,9 @@ public:
|
||||
if (fullView != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(s_device, fullView, nullptr);
|
||||
}
|
||||
if (sampledView != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(s_device, sampledView, nullptr);
|
||||
}
|
||||
for (const auto attachmentView : perMipViews) {
|
||||
if (attachmentView != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(s_device, attachmentView, nullptr);
|
||||
@@ -86,6 +91,7 @@ public:
|
||||
vmaDestroyImage(s_allocator, image, allocation);
|
||||
}
|
||||
fullView = VK_NULL_HANDLE;
|
||||
sampledView = VK_NULL_HANDLE;
|
||||
perMipViews.clear();
|
||||
perMipSampledViews.clear();
|
||||
image = VK_NULL_HANDLE;
|
||||
@@ -127,6 +133,8 @@ public:
|
||||
Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
|
||||
Bool TransitionTextureForStorageImage(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
|
||||
|
||||
static VkImageAspectFlags ResolveSampledImageViewAspectMask(VkImageAspectFlags imageAspect);
|
||||
|
||||
static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
|
||||
VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
|
||||
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
|
||||
|
||||
@@ -1226,6 +1226,127 @@ void main() {
|
||||
const char* value = std::getenv("MOBILEGL_PRESENT_STATS");
|
||||
return value != nullptr && value[0] == '1' && value[1] == '\0';
|
||||
}
|
||||
|
||||
static Bool IsBgraVkFormat(VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) {
|
||||
const SizeT resolvedAlignment = static_cast<SizeT>(std::max(alignment, 1));
|
||||
return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
||||
}
|
||||
|
||||
static Int GetReadbackChannelCount(GLenum format) {
|
||||
switch (format) {
|
||||
case GL_RGB:
|
||||
case GL_BGR:
|
||||
return 3;
|
||||
case GL_RGBA:
|
||||
case GL_BGRA:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void StoreReadbackPixel(const Uint8* src, Bool srcIsBgra, GLenum dstFormat, Uint8* dst) {
|
||||
const Uint8 r = srcIsBgra ? src[2] : src[0];
|
||||
const Uint8 g = src[1];
|
||||
const Uint8 b = srcIsBgra ? src[0] : src[2];
|
||||
const Uint8 a = src[3];
|
||||
|
||||
switch (dstFormat) {
|
||||
case GL_RGB:
|
||||
dst[0] = r;
|
||||
dst[1] = g;
|
||||
dst[2] = b;
|
||||
break;
|
||||
case GL_BGR:
|
||||
dst[0] = b;
|
||||
dst[1] = g;
|
||||
dst[2] = r;
|
||||
break;
|
||||
case GL_RGBA:
|
||||
dst[0] = r;
|
||||
dst[1] = g;
|
||||
dst[2] = b;
|
||||
dst[3] = a;
|
||||
break;
|
||||
case GL_BGRA:
|
||||
dst[0] = b;
|
||||
dst[1] = g;
|
||||
dst[2] = r;
|
||||
dst[3] = a;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Bool PackReadbackToClientOrPbo(const Uint8* srcPixels, VkFormat srcFormat, GLsizei width,
|
||||
GLsizei height, GLenum format, GLenum type, void* pixels) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return true;
|
||||
}
|
||||
if (type != GL_UNSIGNED_BYTE) {
|
||||
MGLOG_E("DirectVulkan readback skipped: unsupported type=0x%x", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
const Int dstChannels = GetReadbackChannelCount(format);
|
||||
if (dstChannels == 0) {
|
||||
MGLOG_E("DirectVulkan readback skipped: unsupported format=0x%x", format);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);
|
||||
const SizeT rowPixels = static_cast<SizeT>(packParams.RowLength > 0 ? packParams.RowLength : width);
|
||||
const SizeT dstRowStride = AlignPixelRow(rowPixels * static_cast<SizeT>(dstChannels),
|
||||
packParams.Alignment);
|
||||
const SizeT dstOffset = static_cast<SizeT>(std::max(packParams.SkipRows, 0)) * dstRowStride +
|
||||
static_cast<SizeT>(std::max(packParams.SkipPixels, 0)) *
|
||||
static_cast<SizeT>(dstChannels);
|
||||
const SizeT packedSize = dstOffset +
|
||||
(static_cast<SizeT>(height - 1) * dstRowStride) +
|
||||
(static_cast<SizeT>(width) * static_cast<SizeT>(dstChannels));
|
||||
Vector<Uint8> packed(packedSize, 0);
|
||||
|
||||
const Bool srcIsBgra = IsBgraVkFormat(srcFormat);
|
||||
for (GLsizei row = 0; row < height; ++row) {
|
||||
const Uint8* srcRow = srcPixels + static_cast<SizeT>(row) * static_cast<SizeT>(width) * 4;
|
||||
Uint8* dstRow = packed.data() + dstOffset + static_cast<SizeT>(row) * dstRowStride;
|
||||
for (GLsizei col = 0; col < width; ++col) {
|
||||
StoreReadbackPixel(srcRow + static_cast<SizeT>(col) * 4,
|
||||
srcIsBgra,
|
||||
format,
|
||||
dstRow + static_cast<SizeT>(col) * static_cast<SizeT>(dstChannels));
|
||||
}
|
||||
}
|
||||
|
||||
const auto& pixelPackBufferObject =
|
||||
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
|
||||
if (pixelPackBufferObject) {
|
||||
const SizeT pboOffset = reinterpret_cast<SizeT>(pixels);
|
||||
if (pboOffset + packed.size() > pixelPackBufferObject->GetSize()) {
|
||||
MGLOG_E("DirectVulkan readback skipped: pixel pack buffer is too small");
|
||||
return false;
|
||||
}
|
||||
pixelPackBufferObject->UploadSubData({packed.data(), packed.size()}, pboOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pixels != nullptr && !packed.empty()) {
|
||||
Memcpy(pixels, packed.data(), packed.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
@@ -1607,7 +1728,10 @@ void main() {
|
||||
++syntheticBinding;
|
||||
}
|
||||
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, static_cast<Uint32>(bindingCount), vkBuffers.data(), vkOffsets.data());
|
||||
if (bindingCount > 0) {
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, static_cast<Uint32>(bindingCount), vkBuffers.data(),
|
||||
vkOffsets.data());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2747,6 +2871,55 @@ void main() {
|
||||
vkCmdDispatch(frame.commandBuffer, numGroupsX, numGroupsY, numGroupsZ);
|
||||
}
|
||||
|
||||
void VulkanRenderer::DispatchComputeIndirect(GLintptr indirect) {
|
||||
m_textureManager->CollectGarbage();
|
||||
auto& frame = m_frameContext.GetCurrent();
|
||||
const auto& program = *MG_State::pGLContext->GetCurrentProgram();
|
||||
ProgramFactory::CompileOptionFlags transformFlags = 0;
|
||||
const auto& programObj = m_programFactory->GetOrCreateProgram(program, transformFlags);
|
||||
|
||||
if (!frame.isCommandRecording) {
|
||||
m_frameContext.BeginCommandRecording();
|
||||
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
}
|
||||
|
||||
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
|
||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||
}
|
||||
|
||||
const VkPipeline pipeline = GetOrCreateComputePipeline(programObj);
|
||||
if (pipeline == VK_NULL_HANDLE) {
|
||||
MGLOG_E("DispatchComputeIndirect skipped: compute pipeline creation failed for program=%u",
|
||||
program.GetExternalIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
|
||||
const Bool boundUniforms = m_uniformManager->BindProgramUniformBuffers(
|
||||
frame.commandBuffer, program, programObj, m_frameContext.GetCurrentFrameIndex(),
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
if (!boundUniforms) {
|
||||
MGLOG_E("DispatchComputeIndirect skipped: BindProgramUniformBuffers failed");
|
||||
return;
|
||||
}
|
||||
|
||||
auto indirectBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DispatchIndirect).GetBoundObject();
|
||||
if (!indirectBuffer) {
|
||||
MGLOG_E("DispatchComputeIndirect skipped: GL_DISPATCH_INDIRECT_BUFFER is not bound");
|
||||
return;
|
||||
}
|
||||
indirectBuffer->MarkPersistentMappedRangeDirty();
|
||||
|
||||
BufferSlice slice{};
|
||||
if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, indirectBuffer, slice)) {
|
||||
MGLOG_E("DispatchComputeIndirect skipped: failed to sync indirect dispatch buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
MGLOG_D("DirectVulkan: glDispatchComputeIndirect(offset=%zu)", static_cast<SizeT>(indirect));
|
||||
vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast<VkDeviceSize>(indirect));
|
||||
}
|
||||
|
||||
void VulkanRenderer::MemoryBarrier(GLbitfield barriers) {
|
||||
auto& frame = m_frameContext.GetCurrent();
|
||||
if (!frame.isCommandRecording) {
|
||||
@@ -2791,17 +2964,16 @@ void main() {
|
||||
m_clearManager->QueueClear(mask, payload, *fbo);
|
||||
}
|
||||
|
||||
void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer,
|
||||
const ClearAttachmentPayload& clearPayload) {
|
||||
void VulkanRenderer::QueueClearBufferPayloadForFramebuffer(
|
||||
const MG_State::GLState::FramebufferObject& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||
const ClearAttachmentPayload& clearPayload) {
|
||||
m_clearManager->CollectGarbage();
|
||||
auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get();
|
||||
MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found");
|
||||
|
||||
auto queueAttachmentClear = [&](FramebufferAttachmentType attachmentType) {
|
||||
if (attachmentType == FramebufferAttachmentType::None) {
|
||||
return;
|
||||
}
|
||||
const auto& attachment = fbo->GetAttachment(attachmentType);
|
||||
const auto& attachment = framebuffer.GetAttachment(attachmentType);
|
||||
if (!attachment.IsTexture() || attachment.IsRenderbuffer()) {
|
||||
return;
|
||||
}
|
||||
@@ -2819,7 +2991,7 @@ void main() {
|
||||
RecordClearBufferError(__func__, ErrorCode::InvalidValue, "color drawbuffer index is out of range");
|
||||
return;
|
||||
}
|
||||
queueAttachmentClear(fbo->GetDrawBuffers()[drawbuffer]);
|
||||
queueAttachmentClear(framebuffer.GetDrawBuffers()[drawbuffer]);
|
||||
return;
|
||||
}
|
||||
case GL_DEPTH:
|
||||
@@ -2850,6 +3022,13 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer,
|
||||
const ClearAttachmentPayload& clearPayload) {
|
||||
auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get();
|
||||
MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found");
|
||||
QueueClearBufferPayloadForFramebuffer(*fbo, buffer, drawbuffer, clearPayload);
|
||||
}
|
||||
|
||||
void VulkanRenderer::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
|
||||
ClearAttachmentPayload payload{};
|
||||
payload.mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
@@ -2878,6 +3057,41 @@ void main() {
|
||||
QueueClearBufferPayload(buffer, drawbuffer, payload);
|
||||
}
|
||||
|
||||
void VulkanRenderer::ClearNamedFramebufferfv(
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||
const GLfloat* value) {
|
||||
if (!framebuffer || value == nullptr) {
|
||||
return;
|
||||
}
|
||||
ClearAttachmentPayload payload{};
|
||||
switch (buffer) {
|
||||
case GL_COLOR:
|
||||
payload.mask = GL_COLOR_BUFFER_BIT;
|
||||
payload.color = FloatVec4(value[0], value[1], value[2], value[3]);
|
||||
break;
|
||||
case GL_DEPTH:
|
||||
payload.mask = GL_DEPTH_BUFFER_BIT;
|
||||
payload.depth = value[0];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
||||
}
|
||||
|
||||
void VulkanRenderer::ClearNamedFramebufferfi(
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||
GLfloat depth, GLint stencil) {
|
||||
if (!framebuffer) {
|
||||
return;
|
||||
}
|
||||
ClearAttachmentPayload payload{};
|
||||
payload.mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
payload.depth = depth;
|
||||
payload.stencil = static_cast<Uint32>(stencil);
|
||||
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
||||
}
|
||||
|
||||
void VulkanRenderer::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
@@ -3137,6 +3351,16 @@ void main() {
|
||||
void VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter) {
|
||||
auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
BlitNamedFramebuffer(readFbo, drawFbo, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
void VulkanRenderer::BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFbo,
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFbo,
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter) {
|
||||
static constexpr GLbitfield kSupportedBlitMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
||||
if ((mask & ~kSupportedBlitMask) != 0) {
|
||||
MGLOG_E("BlitFramebuffer skipped: unsupported mask bits=0x%x", static_cast<Uint32>(mask));
|
||||
@@ -3161,8 +3385,6 @@ void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
MOBILEGL_ASSERT(readFbo != nullptr, "VulkanRenderer::BlitFramebuffer: read framebuffer is null");
|
||||
MOBILEGL_ASSERT(drawFbo != nullptr, "VulkanRenderer::BlitFramebuffer: draw framebuffer is null");
|
||||
|
||||
@@ -3461,10 +3683,18 @@ void main() {
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to restore source image layout", __func__);
|
||||
}
|
||||
|
||||
if (!drawIsDefaultFbo) {
|
||||
VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags dstRestoreAccessMask = 0;
|
||||
GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask);
|
||||
VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags dstRestoreAccessMask = 0;
|
||||
GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask);
|
||||
if (drawIsDefaultFbo) {
|
||||
VkImageLayout dstTrackedLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, dstBinding.image, dstTrackedLayout, dstRestoreLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, dstRestoreAccessMask, dstBinding.aspectMask);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to restore swapchain destination image layout", __func__);
|
||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, dstTrackedLayout);
|
||||
} else {
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, dstRestoreLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask,
|
||||
@@ -3655,6 +3885,287 @@ void main() {
|
||||
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame) {
|
||||
if (frame.isCommandRecording) {
|
||||
m_frameContext.EndCommandRecording();
|
||||
}
|
||||
if (!frame.hasCommandBufferRecorded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
VkSubmitInfo submitInfo{VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
||||
VkSemaphore waitSemaphore = frame.imageAvailableSemaphore;
|
||||
if (!frame.imageAvailableSemaphoreConsumed) {
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = &waitSemaphore;
|
||||
submitInfo.pWaitDstStageMask = &waitDstStageMask;
|
||||
}
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &frame.commandBuffer;
|
||||
|
||||
VkResult result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, frame.imageInFlightFence);
|
||||
if (result != VK_SUCCESS) {
|
||||
MGLOG_E("DirectVulkan readback: vkQueueSubmit returned %d", result);
|
||||
return false;
|
||||
}
|
||||
frame.imageAvailableSemaphoreConsumed = true;
|
||||
|
||||
result = vkWaitForFences(m_device, 1, &frame.imageInFlightFence, VK_TRUE, UINT64_MAX);
|
||||
if (result != VK_SUCCESS) {
|
||||
MGLOG_E("DirectVulkan readback: vkWaitForFences returned %d", result);
|
||||
return false;
|
||||
}
|
||||
result = vkResetFences(m_device, 1, &frame.imageInFlightFence);
|
||||
if (result != VK_SUCCESS) {
|
||||
MGLOG_E("DirectVulkan readback: vkResetFences returned %d", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
frame.hasCommandBufferRecorded = false;
|
||||
frame.isCommandRecording = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanRenderer::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
|
||||
void* pixels) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||
if (readFbo == nullptr) {
|
||||
MGLOG_E("DirectVulkan::ReadPixels skipped: no read framebuffer is bound");
|
||||
return;
|
||||
}
|
||||
|
||||
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 readIsDefaultFbo =
|
||||
(readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
|
||||
BlitImageBinding srcBinding{};
|
||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||
srcBinding)) {
|
||||
return;
|
||||
}
|
||||
if (!readIsDefaultFbo) {
|
||||
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
|
||||
auto sourceTexture = sourceAttachment.GetTexture();
|
||||
if (sourceTexture != nullptr) {
|
||||
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sourceTexture);
|
||||
MOBILEGL_ASSERT(clearReady,
|
||||
"ReadPixels: failed to materialize pending clear for source textureId=%d",
|
||||
sourceTexture->GetExternalIndex());
|
||||
}
|
||||
}
|
||||
|
||||
const VkImageLayout srcOriginalLayout = readIsDefaultFbo
|
||||
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
|
||||
: *srcBinding.trackedLayout;
|
||||
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
MGLOG_E("DirectVulkan::ReadPixels skipped: source image layout is undefined");
|
||||
return;
|
||||
}
|
||||
|
||||
const VkDeviceSize readbackSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(height) * 4;
|
||||
VkBufferObject readback;
|
||||
if (!readback.Create({
|
||||
.allocator = m_allocator,
|
||||
.size = readbackSize,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
|
||||
})) {
|
||||
MGLOG_E("DirectVulkan::ReadPixels skipped: failed to create readback buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
GetImageTransitionSourceState(srcOriginalLayout, srcStageMask, srcAccessMask);
|
||||
if (readIsDefaultFbo) {
|
||||
VkImageLayout trackedLayout = srcOriginalLayout;
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, srcBinding.image, trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain source image", __func__);
|
||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, trackedLayout);
|
||||
} else {
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask,
|
||||
srcBinding.mipLevel, 1);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to transition source image", __func__);
|
||||
}
|
||||
|
||||
VkBufferImageCopy copyRegion{};
|
||||
copyRegion.imageSubresource.aspectMask = srcBinding.aspectMask;
|
||||
copyRegion.imageSubresource.mipLevel = srcBinding.mipLevel;
|
||||
copyRegion.imageSubresource.baseArrayLayer = 0;
|
||||
copyRegion.imageSubresource.layerCount = 1;
|
||||
copyRegion.imageOffset = {x, y, 0};
|
||||
copyRegion.imageExtent = {static_cast<Uint32>(width), static_cast<Uint32>(height), 1};
|
||||
vkCmdCopyImageToBuffer(frame.commandBuffer, srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
readback.GetHandle(), 1, ©Region);
|
||||
|
||||
VkPipelineStageFlags restoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags restoreAccessMask = 0;
|
||||
GetImageTransitionDestinationState(srcOriginalLayout, restoreStageMask, restoreAccessMask);
|
||||
if (readIsDefaultFbo) {
|
||||
VkImageLayout trackedLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, srcBinding.image, trackedLayout, srcOriginalLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask,
|
||||
VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, srcBinding.aspectMask);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to restore swapchain source image layout", __func__);
|
||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, trackedLayout);
|
||||
} else {
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, srcOriginalLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask,
|
||||
VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, srcBinding.aspectMask,
|
||||
srcBinding.mipLevel, 1);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to restore source image layout", __func__);
|
||||
}
|
||||
|
||||
if (!SubmitReadbackCommandsAndWait(frame)) {
|
||||
return;
|
||||
}
|
||||
const auto* mapped = static_cast<const Uint8*>(readback.Map());
|
||||
if (mapped == nullptr) {
|
||||
MGLOG_E("DirectVulkan::ReadPixels skipped: failed to map readback buffer");
|
||||
return;
|
||||
}
|
||||
const VkFormat srcFormat = readIsDefaultFbo ? m_swapchainObject.GetSurfaceFormat().format : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
PackReadbackToClientOrPbo(mapped, srcFormat, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
void VulkanRenderer::GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
||||
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
auto textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
||||
GetTextureImage(textureObject, textureUploadTarget, level, format, type, -1, pixels);
|
||||
}
|
||||
|
||||
void VulkanRenderer::GetTextureImage(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
||||
TextureUploadTarget textureUploadTarget, GLint level, GLenum format,
|
||||
GLenum type, GLsizei bufSize, GLvoid* pixels) {
|
||||
if (textureObject == nullptr || textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||
if (level < 0 || static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
||||
MGLOG_E("DirectVulkan::GetTexImage skipped: level %d is out of range", level);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*textureObject);
|
||||
if (resource == nullptr || resource->image == VK_NULL_HANDLE) {
|
||||
MGLOG_E("DirectVulkan::GetTexImage skipped: failed to sync textureId=%u",
|
||||
textureObject->GetExternalIndex());
|
||||
return;
|
||||
}
|
||||
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) {
|
||||
MGLOG_E("DirectVulkan::GetTexImage skipped: only color textures are supported right now");
|
||||
return;
|
||||
}
|
||||
|
||||
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, *textureObject);
|
||||
MOBILEGL_ASSERT(clearReady,
|
||||
"GetTexImage: failed to materialize pending clear for textureId=%d",
|
||||
textureObject->GetExternalIndex());
|
||||
|
||||
const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, static_cast<Uint>(level));
|
||||
const GLsizei width = texelSize.x();
|
||||
const GLsizei height = texelSize.y();
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
if (bufSize >= 0) {
|
||||
const Int dstChannels = GetReadbackChannelCount(format);
|
||||
if (type == GL_UNSIGNED_BYTE && dstChannels > 0) {
|
||||
const SizeT minSize = static_cast<SizeT>(width) * static_cast<SizeT>(height) *
|
||||
static_cast<SizeT>(dstChannels);
|
||||
if (static_cast<SizeT>(bufSize) < minSize) {
|
||||
MGLOG_E("DirectVulkan::GetTextureImage skipped: destination buffer is too small");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const VkDeviceSize readbackSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(height) * 4;
|
||||
VkBufferObject readback;
|
||||
if (!readback.Create({
|
||||
.allocator = m_allocator,
|
||||
.size = readbackSize,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
|
||||
})) {
|
||||
MGLOG_E("DirectVulkan::GetTexImage skipped: failed to create readback buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
const VkImageLayout originalLayout = resource->layout;
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
GetImageTransitionSourceState(originalLayout, srcStageMask, srcAccessMask);
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, resource->aspect,
|
||||
static_cast<Uint32>(level), 1);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to transition texture image", __func__);
|
||||
|
||||
VkBufferImageCopy copyRegion{};
|
||||
copyRegion.imageSubresource.aspectMask = resource->aspect;
|
||||
copyRegion.imageSubresource.mipLevel = static_cast<Uint32>(level);
|
||||
copyRegion.imageSubresource.baseArrayLayer = 0;
|
||||
copyRegion.imageSubresource.layerCount = 1;
|
||||
copyRegion.imageExtent = {static_cast<Uint32>(width), static_cast<Uint32>(height), 1};
|
||||
vkCmdCopyImageToBuffer(frame.commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
readback.GetHandle(), 1, ©Region);
|
||||
|
||||
VkPipelineStageFlags restoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags restoreAccessMask = 0;
|
||||
GetImageTransitionDestinationState(originalLayout, restoreStageMask, restoreAccessMask);
|
||||
ok = VkTextureManager::TransitionImageLayout(
|
||||
frame.commandBuffer, resource->image, resource->layout, originalLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask,
|
||||
VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, resource->aspect,
|
||||
static_cast<Uint32>(level), 1);
|
||||
MOBILEGL_ASSERT(ok, "%s: failed to restore texture image layout", __func__);
|
||||
|
||||
if (!SubmitReadbackCommandsAndWait(frame)) {
|
||||
return;
|
||||
}
|
||||
const auto* mapped = static_cast<const Uint8*>(readback.Map());
|
||||
if (mapped == nullptr) {
|
||||
MGLOG_E("DirectVulkan::GetTextureImage skipped: failed to map readback buffer");
|
||||
return;
|
||||
}
|
||||
PackReadbackToClientOrPbo(mapped, resource->format, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
const auto uploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
@@ -3927,6 +4438,108 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect,
|
||||
GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) {
|
||||
auto& frame = m_frameContext.GetCurrent();
|
||||
|
||||
if (maxdrawcount <= 0) {
|
||||
return;
|
||||
}
|
||||
if (stride == 0) {
|
||||
stride = sizeof(DrawIndexedCmdParam);
|
||||
}
|
||||
if (stride < static_cast<GLsizei>(sizeof(DrawIndexedCmdParam))) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu",
|
||||
stride, sizeof(DrawIndexedCmdParam));
|
||||
return;
|
||||
}
|
||||
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();
|
||||
const auto* indexBuffer = vao.GetIndexBufferBindingSlot().GetBoundObject().get();
|
||||
if (!indexBuffer) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: no element array buffer is bound");
|
||||
return;
|
||||
}
|
||||
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
const SizeT commandBytes = commandOffset +
|
||||
static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) + sizeof(DrawIndexedCmdParam);
|
||||
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (!drawBuffer || commandBytes > drawBuffer->GetSize()) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
|
||||
if (!parameterBuffer || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
DrawCmdParam vertexRange{};
|
||||
vertexRange.vertexCount = static_cast<Uint32>(indexBuffer->GetSize() / indexSize);
|
||||
vertexRange.instanceCount = 1;
|
||||
|
||||
IndexBufferView indexBufferView{};
|
||||
indexBufferView.indexType = type;
|
||||
indexBufferView.indexByteOffset = 0;
|
||||
indexBufferView.indexByteSize = indexBuffer->GetSize();
|
||||
|
||||
if (!SetupDraw(frame, mode, DrawSetupAspect::IndexBuffer | DrawSetupAspect::IndirectDrawBuffer,
|
||||
vertexRange, &indexBufferView)) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawBuffer->MarkPersistentMappedRangeDirty();
|
||||
parameterBuffer->MarkPersistentMappedRangeDirty();
|
||||
|
||||
BufferSlice drawSlice{};
|
||||
if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, drawBuffer, drawSlice)) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: failed to sync draw indirect buffer");
|
||||
return;
|
||||
}
|
||||
BufferSlice parameterSlice{};
|
||||
if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, parameterBuffer, parameterSlice)) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: failed to sync parameter buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__);
|
||||
if (m_drawIndirectCountExtensionEnabled && s_vkCmdDrawIndexedIndirectCount) {
|
||||
MGLOG_D("DirectVulkan: glMultiDrawElementsIndirectCountARB(max=%d stride=%d)", maxdrawcount, stride);
|
||||
s_vkCmdDrawIndexedIndirectCount(frame.commandBuffer,
|
||||
drawSlice.buffer,
|
||||
drawSlice.offset + static_cast<VkDeviceSize>(commandOffset),
|
||||
parameterSlice.buffer,
|
||||
parameterSlice.offset + static_cast<VkDeviceSize>(drawcount),
|
||||
static_cast<Uint32>(maxdrawcount),
|
||||
static_cast<Uint32>(stride));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto parameterData = parameterBuffer->GetDataReadOnly();
|
||||
const auto drawData = drawBuffer->GetDataReadOnly();
|
||||
if (!parameterData || !drawData) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: CPU fallback cannot read buffers");
|
||||
return;
|
||||
}
|
||||
Uint32 actualDrawCount = 0;
|
||||
std::memcpy(&actualDrawCount, parameterData->data() + drawcount, sizeof(actualDrawCount));
|
||||
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||
for (Uint32 idraw = 0; idraw < actualDrawCount; ++idraw) {
|
||||
DrawIndexedCmdParam cmd{};
|
||||
std::memcpy(&cmd, drawData->data() + commandOffset + static_cast<SizeT>(idraw) * stride, sizeof(cmd));
|
||||
vkCmdDrawIndexed(frame.commandBuffer, cmd.indexCount, cmd.instanceCount, cmd.firstIndex,
|
||||
cmd.vertexOffset, cmd.firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::Present() {
|
||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
||||
"Present, acquired image index out of range");
|
||||
@@ -4354,6 +4967,8 @@ void main() {
|
||||
deviceFeatures.independentBlend = supportedDeviceFeatures.independentBlend;
|
||||
deviceFeatures.shaderClipDistance = supportedDeviceFeatures.shaderClipDistance;
|
||||
deviceFeatures.shaderCullDistance = supportedDeviceFeatures.shaderCullDistance;
|
||||
deviceFeatures.shaderInt64 = supportedDeviceFeatures.shaderInt64;
|
||||
deviceFeatures.drawIndirectFirstInstance = supportedDeviceFeatures.drawIndirectFirstInstance;
|
||||
|
||||
VkDeviceCreateInfo deviceCreateInfo{};
|
||||
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
@@ -4418,16 +5033,20 @@ void main() {
|
||||
|
||||
deviceCreateInfo.enabledExtensionCount = static_cast<Uint32>(enabledDeviceExtensions.size());
|
||||
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
|
||||
MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s",
|
||||
MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s shaderInt64=%s drawIndirectFirstInstance=%s",
|
||||
supportedDeviceFeatures.geometryShader ? "true" : "false",
|
||||
supportedDeviceFeatures.independentBlend ? "true" : "false",
|
||||
supportedDeviceFeatures.shaderClipDistance ? "true" : "false",
|
||||
supportedDeviceFeatures.shaderCullDistance ? "true" : "false");
|
||||
MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s",
|
||||
supportedDeviceFeatures.shaderCullDistance ? "true" : "false",
|
||||
supportedDeviceFeatures.shaderInt64 ? "true" : "false",
|
||||
supportedDeviceFeatures.drawIndirectFirstInstance ? "true" : "false");
|
||||
MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s shaderInt64=%s drawIndirectFirstInstance=%s",
|
||||
deviceFeatures.geometryShader ? "true" : "false",
|
||||
deviceFeatures.independentBlend ? "true" : "false",
|
||||
deviceFeatures.shaderClipDistance ? "true" : "false",
|
||||
deviceFeatures.shaderCullDistance ? "true" : "false");
|
||||
deviceFeatures.shaderCullDistance ? "true" : "false",
|
||||
deviceFeatures.shaderInt64 ? "true" : "false",
|
||||
deviceFeatures.drawIndirectFirstInstance ? "true" : "false");
|
||||
VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice");
|
||||
|
||||
s_vkCmdDrawIndexedIndirectCount = reinterpret_cast<PFNDrawIndexedIndirectCountFunc>(
|
||||
@@ -4634,6 +5253,8 @@ void main() {
|
||||
Vector<const char*>& inOutEnabledExtensions) {
|
||||
m_drawIndirectCountExtensionEnabled = EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions,
|
||||
VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME);
|
||||
EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions,
|
||||
VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME);
|
||||
#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
|
||||
EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions,
|
||||
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
|
||||
|
||||
@@ -120,17 +120,34 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter);
|
||||
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFbo,
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFbo,
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter);
|
||||
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
||||
GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
void GenerateMipmap(GLenum target);
|
||||
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 GetTextureImage(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
|
||||
TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type,
|
||||
GLsizei bufSize, GLvoid* pixels);
|
||||
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
|
||||
void DispatchComputeIndirect(GLintptr indirect);
|
||||
void MemoryBarrier(GLbitfield barriers);
|
||||
void DrawArrays(const DrawCmd& payload);
|
||||
void DrawElements(const DrawIndexedCmd& payload);
|
||||
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
||||
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride);
|
||||
void Present();
|
||||
|
||||
const PhysicalDevice& GetPhysicalDevice() const;
|
||||
@@ -173,6 +190,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
};
|
||||
|
||||
void QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload);
|
||||
void QueueClearBufferPayloadForFramebuffer(const MG_State::GLState::FramebufferObject& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer,
|
||||
const ClearAttachmentPayload& clearPayload);
|
||||
|
||||
NativeWindowType m_window = 0;
|
||||
void* m_platformDisplay = nullptr;
|
||||
@@ -279,6 +299,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const IntVec3& storageBaseTexelSize,
|
||||
VkImageLayout originalLayout,
|
||||
VkImageLayout finalLayout);
|
||||
Bool SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame);
|
||||
|
||||
void ShutdownSwapchain();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user