mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Fix] (MG_Backend/DirectVulkan): fix some uniform/sampler binding
This commit is contained in:
@@ -19,6 +19,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.vertexInputHash, sizeof(payload.vertexInputHash)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.pipelineLayout, sizeof(payload.pipelineLayout)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.colorAttachmentCount, sizeof(payload.colorAttachmentCount)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.subpass, sizeof(payload.subpass)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.topology, sizeof(payload.topology)));
|
||||
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.cullMode, sizeof(payload.cullMode)));
|
||||
@@ -61,6 +62,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MOBILEGL_ASSERT(payload.vertexInputState != nullptr, "PipelineFactory: vertexInputState is null");
|
||||
MOBILEGL_ASSERT(payload.pipelineLayout != VK_NULL_HANDLE, "PipelineFactory: pipelineLayout is null");
|
||||
MOBILEGL_ASSERT(payload.renderPass != VK_NULL_HANDLE, "PipelineFactory: renderPass is null");
|
||||
MOBILEGL_ASSERT(payload.colorAttachmentCount <= 32,
|
||||
"PipelineFactory: colorAttachmentCount=%u is unexpectedly large",
|
||||
payload.colorAttachmentCount);
|
||||
MGLOG_D("PipelineFactory::CreatePipeline: programHash=0x%llx vertexInputHash=0x%llx colorAttachmentCount=%u subpass=%u",
|
||||
static_cast<unsigned long long>(payload.programHash),
|
||||
static_cast<unsigned long long>(payload.vertexInputHash),
|
||||
payload.colorAttachmentCount,
|
||||
payload.subpass);
|
||||
|
||||
static constexpr VkDynamicState kDynamicStates[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
@@ -95,18 +104,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
depthStencil.depthBoundsTestEnable = VK_FALSE;
|
||||
depthStencil.stencilTestEnable = VK_FALSE;
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorAttach{};
|
||||
colorAttach.colorWriteMask = payload.colorWriteMask;
|
||||
colorAttach.blendEnable = payload.blendEnable ? VK_TRUE : VK_FALSE;
|
||||
colorAttach.srcColorBlendFactor = payload.srcColorBlendFactor;
|
||||
colorAttach.dstColorBlendFactor = payload.dstColorBlendFactor;
|
||||
colorAttach.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
colorAttach.srcAlphaBlendFactor = payload.srcAlphaBlendFactor;
|
||||
colorAttach.dstAlphaBlendFactor = payload.dstAlphaBlendFactor;
|
||||
colorAttach.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
VkPipelineColorBlendAttachmentState colorAttachTemplate{};
|
||||
colorAttachTemplate.colorWriteMask = payload.colorWriteMask;
|
||||
colorAttachTemplate.blendEnable = payload.blendEnable ? VK_TRUE : VK_FALSE;
|
||||
colorAttachTemplate.srcColorBlendFactor = payload.srcColorBlendFactor;
|
||||
colorAttachTemplate.dstColorBlendFactor = payload.dstColorBlendFactor;
|
||||
colorAttachTemplate.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
colorAttachTemplate.srcAlphaBlendFactor = payload.srcAlphaBlendFactor;
|
||||
colorAttachTemplate.dstAlphaBlendFactor = payload.dstAlphaBlendFactor;
|
||||
colorAttachTemplate.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
Vector<VkPipelineColorBlendAttachmentState> colorAttachments(payload.colorAttachmentCount,
|
||||
colorAttachTemplate);
|
||||
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
||||
blend.attachmentCount = 1;
|
||||
blend.pAttachments = &colorAttach;
|
||||
blend.attachmentCount = payload.colorAttachmentCount;
|
||||
blend.pAttachments = colorAttachments.empty() ? nullptr : colorAttachments.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo gpi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
||||
gpi.stageCount = static_cast<Uint32>(payload.stages->size());
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
HashType vertexInputHash = 0;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
VkRenderPass renderPass = VK_NULL_HANDLE;
|
||||
Uint32 colorAttachmentCount = 1;
|
||||
Uint32 subpass = 0;
|
||||
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
|
||||
@@ -27,6 +27,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
using SpvcSession = MG_Util::ShaderTranspiler::SpvcSession;
|
||||
using SessionUsageBit = MG_Util::ShaderTranspiler::SessionUsageBit;
|
||||
|
||||
struct DescriptorKey {
|
||||
ProgramFactory::DescriptorBindingKind kind = ProgramFactory::DescriptorBindingKind::None;
|
||||
String name;
|
||||
|
||||
Bool operator==(const DescriptorKey& other) const {
|
||||
return kind == other.kind && name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
struct DescriptorKeyHash {
|
||||
SizeT operator()(const DescriptorKey& key) const noexcept {
|
||||
return std::hash<String>{}(key.name) ^ (static_cast<SizeT>(key.kind) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct PositionTargetInfo {
|
||||
Uint32 variableId = 0;
|
||||
Uint32 vectorTypeId = 0;
|
||||
@@ -324,6 +339,193 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (hasVertex) return ShaderStage::Vertex;
|
||||
return ShaderStage::Unknown;
|
||||
}
|
||||
|
||||
ProgramFactory::DescriptorBindingKind ReflectDescriptorTypeToBindingKind(SpvReflectDescriptorType descriptorType) {
|
||||
switch (descriptorType) {
|
||||
case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
return ProgramFactory::DescriptorBindingKind::UniformBufferDynamic;
|
||||
case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
return ProgramFactory::DescriptorBindingKind::CombinedImageSampler;
|
||||
default:
|
||||
MOBILEGL_ASSERT(false, "ProgramFactory: unsupported reflected descriptor type %d",
|
||||
static_cast<Int>(descriptorType));
|
||||
return ProgramFactory::DescriptorBindingKind::None;
|
||||
}
|
||||
}
|
||||
|
||||
String NormalizeDescriptorName(const SpvReflectDescriptorBinding& binding,
|
||||
ProgramFactory::DescriptorBindingKind kind) {
|
||||
const char* rawName = binding.name;
|
||||
if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic &&
|
||||
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;
|
||||
if (kind == ProgramFactory::DescriptorBindingKind::CombinedImageSampler) {
|
||||
const auto arraySuffix = name.find("[0]");
|
||||
if (arraySuffix != String::npos) {
|
||||
name = name.substr(0, arraySuffix);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
Bool RemapDescriptorBindingsForVulkan(const Vector<Vector<Uint>>& inputModules, Uint32 maxBindings,
|
||||
Vector<Vector<Uint>>& outputModules) {
|
||||
outputModules = inputModules;
|
||||
|
||||
Vector<SpvReflectShaderModule> reflectModules(outputModules.size());
|
||||
Vector<Bool> reflectModuleValid(outputModules.size(), false);
|
||||
UnorderedMap<DescriptorKey, Uint32, DescriptorKeyHash> assignedBindings;
|
||||
Uint32 nextBinding = 0;
|
||||
|
||||
const auto destroyReflectModules = [&]() {
|
||||
for (SizeT moduleIndex = 0; moduleIndex < reflectModules.size(); ++moduleIndex) {
|
||||
if (!reflectModuleValid[moduleIndex]) {
|
||||
continue;
|
||||
}
|
||||
spvReflectDestroyShaderModule(&reflectModules[moduleIndex]);
|
||||
reflectModuleValid[moduleIndex] = false;
|
||||
}
|
||||
};
|
||||
|
||||
for (SizeT moduleIndex = 0; moduleIndex < outputModules.size(); ++moduleIndex) {
|
||||
auto& moduleSpv = outputModules[moduleIndex];
|
||||
if (moduleSpv.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const SpvReflectResult createResult =
|
||||
spvReflectCreateShaderModule(moduleSpv.size() * sizeof(Uint), moduleSpv.data(),
|
||||
&reflectModules[moduleIndex]);
|
||||
MOBILEGL_ASSERT(createResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory: failed to create reflection module for stage %zu (result=%d)",
|
||||
moduleIndex, static_cast<Int>(createResult));
|
||||
if (createResult != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
destroyReflectModules();
|
||||
return false;
|
||||
}
|
||||
reflectModuleValid[moduleIndex] = true;
|
||||
|
||||
uint32_t bindingCount = 0;
|
||||
SpvReflectResult reflectResult =
|
||||
spvReflectEnumerateDescriptorBindings(&reflectModules[moduleIndex], &bindingCount, nullptr);
|
||||
MOBILEGL_ASSERT(reflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory: failed to enumerate descriptor bindings for stage %zu (result=%d)",
|
||||
moduleIndex, static_cast<Int>(reflectResult));
|
||||
if (reflectResult != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
destroyReflectModules();
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<SpvReflectDescriptorBinding*> bindings(bindingCount);
|
||||
if (bindingCount > 0) {
|
||||
reflectResult = spvReflectEnumerateDescriptorBindings(&reflectModules[moduleIndex], &bindingCount,
|
||||
bindings.data());
|
||||
MOBILEGL_ASSERT(
|
||||
reflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory: failed to fetch descriptor bindings for stage %zu (result=%d)", moduleIndex,
|
||||
static_cast<Int>(reflectResult));
|
||||
if (reflectResult != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
destroyReflectModules();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(bindings.begin(), bindings.end(), [](const auto* lhs, const auto* rhs) {
|
||||
if (lhs->set != rhs->set) {
|
||||
return lhs->set < rhs->set;
|
||||
}
|
||||
if (lhs->binding != rhs->binding) {
|
||||
return lhs->binding < rhs->binding;
|
||||
}
|
||||
return lhs->spirv_id < rhs->spirv_id;
|
||||
});
|
||||
|
||||
for (auto* binding : bindings) {
|
||||
MOBILEGL_ASSERT(binding != nullptr, "ProgramFactory: null descriptor binding reflection record");
|
||||
const auto kind = ReflectDescriptorTypeToBindingKind(binding->descriptor_type);
|
||||
MOBILEGL_ASSERT(binding->count == 1,
|
||||
"ProgramFactory: descriptor arrays are unsupported (name='%s' count=%u)",
|
||||
binding->name ? binding->name : "<null>", binding->count);
|
||||
|
||||
DescriptorKey key{};
|
||||
key.kind = kind;
|
||||
key.name = NormalizeDescriptorName(*binding, kind);
|
||||
|
||||
Uint32 assignedBinding = 0;
|
||||
const auto it = assignedBindings.find(key);
|
||||
if (it == assignedBindings.end()) {
|
||||
MOBILEGL_ASSERT(nextBinding < maxBindings,
|
||||
"ProgramFactory: reflected descriptor count exceeded maxBindings (%u >= %u)",
|
||||
nextBinding, maxBindings);
|
||||
assignedBinding = nextBinding;
|
||||
assignedBindings.emplace(key, assignedBinding);
|
||||
++nextBinding;
|
||||
} else {
|
||||
assignedBinding = it->second;
|
||||
}
|
||||
|
||||
if (binding->binding != assignedBinding || binding->set != 0) {
|
||||
reflectResult = spvReflectChangeDescriptorBindingNumbers(&reflectModules[moduleIndex], binding,
|
||||
assignedBinding, 0);
|
||||
MOBILEGL_ASSERT(reflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory: failed to remap descriptor '%s' in stage %zu (result=%d)",
|
||||
key.name.c_str(), moduleIndex, static_cast<Int>(reflectResult));
|
||||
if (reflectResult != SPV_REFLECT_RESULT_SUCCESS) {
|
||||
destroyReflectModules();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SizeT moduleIndex = 0; moduleIndex < outputModules.size(); ++moduleIndex) {
|
||||
if (!reflectModuleValid[moduleIndex]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Uint32 codeSizeBytes = spvReflectGetCodeSize(&reflectModules[moduleIndex]);
|
||||
MOBILEGL_ASSERT((codeSizeBytes % sizeof(Uint)) == 0,
|
||||
"ProgramFactory: reflected SPIR-V size is not word aligned for stage %zu",
|
||||
moduleIndex);
|
||||
const Uint32* code = spvReflectGetCode(&reflectModules[moduleIndex]);
|
||||
MOBILEGL_ASSERT(code != nullptr, "ProgramFactory: reflected SPIR-V code pointer is null for stage %zu",
|
||||
moduleIndex);
|
||||
outputModules[moduleIndex].assign(code, code + (codeSizeBytes / sizeof(Uint)));
|
||||
}
|
||||
|
||||
destroyReflectModules();
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureTarget ReflectImageTraitsToTextureTarget(const SpvReflectImageTraits& imageTraits) {
|
||||
switch (imageTraits.dim) {
|
||||
case SpvDim1D:
|
||||
return imageTraits.arrayed != 0 ? TextureTarget::Texture1DArray : TextureTarget::Texture1D;
|
||||
case SpvDim2D:
|
||||
if (imageTraits.ms != 0) {
|
||||
return imageTraits.arrayed != 0 ? TextureTarget::Texture2DMultisampleArray
|
||||
: TextureTarget::Texture2DMultisample;
|
||||
}
|
||||
return imageTraits.arrayed != 0 ? TextureTarget::Texture2DArray : TextureTarget::Texture2D;
|
||||
case SpvDim3D:
|
||||
return TextureTarget::Texture3D;
|
||||
case SpvDimCube:
|
||||
return imageTraits.arrayed != 0 ? TextureTarget::TextureCubeMapArray : TextureTarget::TextureCubeMap;
|
||||
case SpvDimBuffer:
|
||||
return TextureTarget::TextureBuffer;
|
||||
default:
|
||||
MOBILEGL_ASSERT(false, "ProgramFactory: unsupported sampler image dim %d", imageTraits.dim);
|
||||
return TextureTarget::Unknown;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VkShaderStageFlagBits ProgramFactory::ToVkStage(ShaderStage stage) {
|
||||
@@ -419,70 +621,137 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void ProgramFactory::ReflectLayout(const MG_State::GLState::ProgramObject& program,
|
||||
VkProgramObject& entry) const {
|
||||
const Vector<Vector<Uint>>& spirv, VkProgramObject& entry) const {
|
||||
// Initialize layout vectors
|
||||
entry.bindingKinds.assign(m_maxBindings, DescriptorBindingKind::None);
|
||||
entry.uniformBlockIndexByBinding.assign(m_maxBindings, -1);
|
||||
entry.samplerNameByBinding.assign(m_maxBindings, String());
|
||||
entry.samplerUniformLocationByBinding.assign(m_maxBindings, -1);
|
||||
entry.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D);
|
||||
entry.globalUboBinding = -1;
|
||||
entry.dynamicBindings.clear();
|
||||
|
||||
// Use SpvcSession (Reflection mode) to reflect all SPIR-V modules in a single pass per module
|
||||
const auto& spirv = program.GetGeneratedSpirv();
|
||||
for (const auto& module : spirv) {
|
||||
if (module.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SpvcSession session(module, SessionUsageBit::Reflection);
|
||||
SpvReflectShaderModule reflectModule{};
|
||||
const SpvReflectResult createReflectResult =
|
||||
spvReflectCreateShaderModule(module.size() * sizeof(Uint), module.data(), &reflectModule);
|
||||
MOBILEGL_ASSERT(createReflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory::ReflectLayout: failed to create reflection module (result=%d)",
|
||||
static_cast<Int>(createReflectResult));
|
||||
|
||||
// Reflect uniform buffers
|
||||
auto ubos = session.GetShaderInterface(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER);
|
||||
for (const auto& ubo : ubos) {
|
||||
const Uint32 binding = ubo.location; // GetShaderInterface stores binding in location field
|
||||
if (binding >= m_maxBindings) {
|
||||
continue;
|
||||
}
|
||||
if (entry.bindingKinds[binding] == DescriptorBindingKind::None) {
|
||||
entry.bindingKinds[binding] = DescriptorBindingKind::UniformBufferDynamic;
|
||||
}
|
||||
MOBILEGL_ASSERT(binding < m_maxBindings,
|
||||
"ProgramFactory::ReflectLayout: UBO binding %u exceeds maxBindings=%u for '%s'",
|
||||
binding, m_maxBindings, ubo.name.c_str());
|
||||
|
||||
MOBILEGL_ASSERT(entry.bindingKinds[binding] == DescriptorBindingKind::None ||
|
||||
entry.bindingKinds[binding] == DescriptorBindingKind::UniformBufferDynamic,
|
||||
"ProgramFactory::ReflectLayout: descriptor binding %u has conflicting kinds for UBO '%s'",
|
||||
binding, ubo.name.c_str());
|
||||
entry.bindingKinds[binding] = DescriptorBindingKind::UniformBufferDynamic;
|
||||
|
||||
// Check for global UBO
|
||||
if (entry.globalUboBinding < 0 &&
|
||||
std::strstr(ubo.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) {
|
||||
if (std::strstr(ubo.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) {
|
||||
MOBILEGL_ASSERT(entry.globalUboBinding < 0 || entry.globalUboBinding == static_cast<Int>(binding),
|
||||
"ProgramFactory::ReflectLayout: global UBO binding mismatch (%d vs %u)",
|
||||
entry.globalUboBinding, binding);
|
||||
MOBILEGL_ASSERT(entry.uniformBlockIndexByBinding[binding] < 0,
|
||||
"ProgramFactory::ReflectLayout: global UBO shares binding %u with regular UBO index %d",
|
||||
binding, entry.uniformBlockIndexByBinding[binding]);
|
||||
entry.globalUboBinding = static_cast<Int>(binding);
|
||||
continue;
|
||||
}
|
||||
|
||||
const Uint blockIndex = program.GetUniformBlockIndex(ubo.name.c_str());
|
||||
MOBILEGL_ASSERT(blockIndex != 0xFFFFFFFFu,
|
||||
"ProgramFactory::ReflectLayout: failed to resolve uniform block '%s'", ubo.name.c_str());
|
||||
MOBILEGL_ASSERT(entry.globalUboBinding != static_cast<Int>(binding),
|
||||
"ProgramFactory::ReflectLayout: regular UBO '%s' collides with global UBO binding %u",
|
||||
ubo.name.c_str(), binding);
|
||||
MOBILEGL_ASSERT(entry.uniformBlockIndexByBinding[binding] < 0 ||
|
||||
entry.uniformBlockIndexByBinding[binding] == static_cast<Int>(blockIndex),
|
||||
"ProgramFactory::ReflectLayout: descriptor binding %u maps to conflicting UBO blocks (%d vs %u)",
|
||||
binding, entry.uniformBlockIndexByBinding[binding], blockIndex);
|
||||
entry.uniformBlockIndexByBinding[binding] = static_cast<Int>(blockIndex);
|
||||
}
|
||||
|
||||
// Reflect sampled images
|
||||
auto samplers = session.GetShaderInterface(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE);
|
||||
for (const auto& sampler : samplers) {
|
||||
const Uint32 binding = sampler.location; // GetShaderInterface stores binding in location field
|
||||
if (binding >= m_maxBindings) {
|
||||
uint32_t reflectedBindingCount = 0;
|
||||
SpvReflectResult reflectResult =
|
||||
spvReflectEnumerateDescriptorBindings(&reflectModule, &reflectedBindingCount, nullptr);
|
||||
MOBILEGL_ASSERT(reflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory::ReflectLayout: failed to enumerate descriptor bindings (result=%d)",
|
||||
static_cast<Int>(reflectResult));
|
||||
|
||||
Vector<SpvReflectDescriptorBinding*> reflectedBindings(reflectedBindingCount);
|
||||
if (reflectedBindingCount > 0) {
|
||||
reflectResult = spvReflectEnumerateDescriptorBindings(&reflectModule, &reflectedBindingCount,
|
||||
reflectedBindings.data());
|
||||
MOBILEGL_ASSERT(
|
||||
reflectResult == SPV_REFLECT_RESULT_SUCCESS,
|
||||
"ProgramFactory::ReflectLayout: failed to fetch descriptor bindings (result=%d)",
|
||||
static_cast<Int>(reflectResult));
|
||||
}
|
||||
|
||||
for (const auto* sampler : reflectedBindings) {
|
||||
if (sampler == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (sampler->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER &&
|
||||
sampler->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sampler always wins over UBO for a binding slot
|
||||
const Uint32 binding = sampler->binding;
|
||||
const String uniformName = NormalizeDescriptorName(*sampler, DescriptorBindingKind::CombinedImageSampler);
|
||||
MOBILEGL_ASSERT(binding < m_maxBindings,
|
||||
"ProgramFactory::ReflectLayout: sampler binding %u exceeds maxBindings=%u for '%s'",
|
||||
binding, m_maxBindings, uniformName.c_str());
|
||||
|
||||
MOBILEGL_ASSERT(entry.bindingKinds[binding] == DescriptorBindingKind::None ||
|
||||
entry.bindingKinds[binding] == DescriptorBindingKind::CombinedImageSampler,
|
||||
"ProgramFactory::ReflectLayout: descriptor binding %u has conflicting kinds for sampler '%s'",
|
||||
binding, uniformName.c_str());
|
||||
entry.bindingKinds[binding] = DescriptorBindingKind::CombinedImageSampler;
|
||||
|
||||
// Resolve uniform location for this sampler
|
||||
String uniformName = sampler.name;
|
||||
Int location = program.GetUniformLocation(uniformName);
|
||||
if (location < 0) {
|
||||
const auto arraySuffix = uniformName.find("[0]");
|
||||
if (arraySuffix != String::npos) {
|
||||
uniformName = uniformName.substr(0, arraySuffix);
|
||||
location = program.GetUniformLocation(uniformName);
|
||||
}
|
||||
}
|
||||
if (location < 0) {
|
||||
continue;
|
||||
}
|
||||
const Int location = program.GetUniformLocation(uniformName);
|
||||
const TextureTarget target =
|
||||
location >= 0 ? UniformTypeToTextureTarget(program.GetUniformType(static_cast<Uint>(location)))
|
||||
: ReflectImageTraitsToTextureTarget(sampler->image);
|
||||
MOBILEGL_ASSERT(target != TextureTarget::Unknown,
|
||||
"ProgramFactory::ReflectLayout: failed to resolve sampler target for '%s'",
|
||||
uniformName.c_str());
|
||||
MOBILEGL_ASSERT(entry.samplerUniformLocationByBinding[binding] < 0 || location < 0 ||
|
||||
entry.samplerUniformLocationByBinding[binding] == location,
|
||||
"ProgramFactory::ReflectLayout: sampler binding %u maps to conflicting uniform locations (%d vs %d)",
|
||||
binding, entry.samplerUniformLocationByBinding[binding], location);
|
||||
MOBILEGL_ASSERT(entry.samplerUniformLocationByBinding[binding] < 0 ||
|
||||
entry.samplerTextureTargetByBinding[binding] == target,
|
||||
"ProgramFactory::ReflectLayout: sampler binding %u maps to conflicting texture targets (%d vs %d)",
|
||||
binding, static_cast<Int>(entry.samplerTextureTargetByBinding[binding]),
|
||||
static_cast<Int>(target));
|
||||
MOBILEGL_ASSERT(entry.samplerNameByBinding[binding].empty() ||
|
||||
entry.samplerNameByBinding[binding] == uniformName,
|
||||
"ProgramFactory::ReflectLayout: sampler binding %u maps to conflicting names ('%s' vs '%s')",
|
||||
binding, entry.samplerNameByBinding[binding].c_str(), uniformName.c_str());
|
||||
|
||||
entry.samplerUniformLocationByBinding[binding] = location;
|
||||
entry.samplerTextureTargetByBinding[binding] =
|
||||
UniformTypeToTextureTarget(program.GetUniformType(static_cast<Uint>(location)));
|
||||
if (location >= 0) {
|
||||
entry.samplerUniformLocationByBinding[binding] = location;
|
||||
}
|
||||
entry.samplerNameByBinding[binding] = uniformName;
|
||||
entry.samplerTextureTargetByBinding[binding] = target;
|
||||
}
|
||||
|
||||
spvReflectDestroyShaderModule(&reflectModule);
|
||||
}
|
||||
|
||||
// Build Vulkan descriptor set layout and pipeline layout from reflected binding kinds
|
||||
@@ -535,6 +804,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
entry.hash = hash;
|
||||
auto& shaders = program.GetAttachedShaders();
|
||||
auto& spirv = program.GetGeneratedSpirv();
|
||||
Vector<Vector<Uint>> moduleSpirvs(spirv.size());
|
||||
|
||||
const ShaderStage fixupStage = PickClipFixupStage(shaders);
|
||||
|
||||
@@ -542,14 +812,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
auto& spv = spirv[i];
|
||||
if (spv.empty()) continue;
|
||||
|
||||
Vector<Uint> moduleSpv;
|
||||
|
||||
// Apply position fixup if needed
|
||||
if (fixupStage != ShaderStage::Unknown && shaders[i] && shaders[i]->GetShaderStage() == fixupStage) {
|
||||
TransformSpirvForVulkanPositionFix(spv, moduleSpv, flags);
|
||||
TransformSpirvForVulkanPositionFix(spv, moduleSpirvs[i], flags);
|
||||
} else {
|
||||
moduleSpv = spv;
|
||||
moduleSpirvs[i] = spv;
|
||||
}
|
||||
}
|
||||
|
||||
const Bool remapOk = RemapDescriptorBindingsForVulkan(moduleSpirvs, m_maxBindings, moduleSpirvs);
|
||||
MOBILEGL_ASSERT(remapOk, "ProgramFactory::GetOrCreateProgram: descriptor binding remap failed");
|
||||
|
||||
for (SizeT i = 0; i < shaders.size(); ++i) {
|
||||
auto& moduleSpv = moduleSpirvs[i];
|
||||
if (moduleSpv.empty()) continue;
|
||||
|
||||
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||
smci.codeSize = moduleSpv.size() * sizeof(Uint);
|
||||
@@ -569,7 +845,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
// Reflect and create layout as part of the program object
|
||||
ReflectLayout(program, entry);
|
||||
ReflectLayout(program, moduleSpirvs, entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
Vector<DescriptorBindingKind> bindingKinds;
|
||||
Vector<Uint32> dynamicBindings;
|
||||
Vector<Int> uniformBlockIndexByBinding;
|
||||
Vector<String> samplerNameByBinding;
|
||||
Vector<Int> samplerUniformLocationByBinding;
|
||||
Vector<TextureTarget> samplerTextureTargetByBinding;
|
||||
Int globalUboBinding = -1;
|
||||
@@ -62,6 +64,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pipelineLayout = other.pipelineLayout;
|
||||
bindingKinds = std::move(other.bindingKinds);
|
||||
dynamicBindings = std::move(other.dynamicBindings);
|
||||
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
|
||||
samplerNameByBinding = std::move(other.samplerNameByBinding);
|
||||
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
|
||||
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
|
||||
globalUboBinding = other.globalUboBinding;
|
||||
@@ -82,6 +86,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pipelineLayout = other.pipelineLayout;
|
||||
bindingKinds = std::move(other.bindingKinds);
|
||||
dynamicBindings = std::move(other.dynamicBindings);
|
||||
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
|
||||
samplerNameByBinding = std::move(other.samplerNameByBinding);
|
||||
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
|
||||
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
|
||||
globalUboBinding = other.globalUboBinding;
|
||||
@@ -133,7 +139,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
private:
|
||||
static TextureTarget UniformTypeToTextureTarget(GLenum glType);
|
||||
void ReflectLayout(const MG_State::GLState::ProgramObject& program, VkProgramObject& entry) const;
|
||||
void ReflectLayout(const MG_State::GLState::ProgramObject& program, const Vector<Vector<Uint>>& spirv,
|
||||
VkProgramObject& entry) const;
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
Uint32 m_maxBindings = 0;
|
||||
|
||||
@@ -53,6 +53,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
static Int ResolveSamplerUnitIndex(const MG_State::GLState::ProgramObject& program, Int location, Uint32 binding) {
|
||||
MOBILEGL_ASSERT(location >= -1, "ResolveSamplerUnitIndex: invalid sampler location for binding %u", binding);
|
||||
if (location < 0) {
|
||||
return 0;
|
||||
}
|
||||
const Int uniformUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
MOBILEGL_ASSERT(uniformUnit >= -1,
|
||||
"ResolveSamplerUnitIndex: invalid texture unit for binding %u location %d (unit=%d)", binding,
|
||||
location, uniformUnit);
|
||||
return uniformUnit >= 0 ? uniformUnit : 0;
|
||||
}
|
||||
|
||||
Bool UniformManager::Initialize(VkDevice device, VkBufferManager* bufferManager,
|
||||
ProgramFactory* programFactory,
|
||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||
@@ -164,28 +176,34 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
(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(),
|
||||
"ResolveSamplerDescriptor: sampler binding %u name lookup out of range", binding);
|
||||
SharedPtr<MG_State::GLState::ITextureObject> texture;
|
||||
if (!ResolveSamplerTexture(program, programObj, binding, texture)) {
|
||||
return false;
|
||||
}
|
||||
const Bool resolvedTexture = ResolveSamplerTexture(program, programObj, binding, texture);
|
||||
MOBILEGL_ASSERT(resolvedTexture,
|
||||
"ResolveSamplerDescriptor: failed to resolve sampler texture for binding %u ('%s')", binding,
|
||||
programObj.samplerNameByBinding[binding].c_str());
|
||||
|
||||
const Int location = programObj.samplerUniformLocationByBinding[binding];
|
||||
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
const Int unit = ResolveSamplerUnitIndex(program, location, binding);
|
||||
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
|
||||
const auto samplerOverride = textureUnit.GetSamplerObject();
|
||||
if (!texture) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(texture != nullptr,
|
||||
"ResolveSamplerDescriptor: sampler binding %u ('%s') resolved null texture (location=%d unit=%d target=%d)",
|
||||
binding, programObj.samplerNameByBinding[binding].c_str(), location, unit,
|
||||
static_cast<Int>(programObj.samplerTextureTargetByBinding[binding]));
|
||||
|
||||
const MG_State::GLState::SamplerObject* samplerToUse =
|
||||
samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get();
|
||||
if (!samplerToUse) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(samplerToUse != nullptr,
|
||||
"ResolveSamplerDescriptor: sampler binding %u ('%s') has no sampler object (textureId=%d location=%d unit=%d)",
|
||||
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(), location,
|
||||
unit);
|
||||
VkTextureManager::TextureResource* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
||||
if (resource == nullptr) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(resource != nullptr,
|
||||
"ResolveSamplerDescriptor: sampler binding %u ('%s') failed to create/sync texture resource (textureId=%d target=%d location=%d unit=%d)",
|
||||
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
|
||||
static_cast<Int>(texture->GetTarget()), location, unit);
|
||||
if (!IsValidSampledImageLayout(resource->layout)) {
|
||||
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
FramebufferAttachmentType attachmentType = FramebufferAttachmentType::None;
|
||||
@@ -218,14 +236,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const SamplerBindingOverride& samplerBindingOverride, VkDescriptorImageInfo& outImageInfo) const {
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptorOverride: texture manager is null");
|
||||
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptorOverride: sampler manager is null");
|
||||
if (samplerBindingOverride.texture == nullptr || samplerBindingOverride.sampler == nullptr) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(samplerBindingOverride.texture != nullptr,
|
||||
"ResolveSamplerDescriptorOverride: override texture is null for binding %u",
|
||||
samplerBindingOverride.binding);
|
||||
MOBILEGL_ASSERT(samplerBindingOverride.sampler != nullptr,
|
||||
"ResolveSamplerDescriptorOverride: override sampler is null for binding %u",
|
||||
samplerBindingOverride.binding);
|
||||
|
||||
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*samplerBindingOverride.texture);
|
||||
if (resource == nullptr || !IsValidSampledImageLayout(resource->layout)) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(resource != nullptr,
|
||||
"ResolveSamplerDescriptorOverride: failed to sync override texture resource for binding %u textureId=%d",
|
||||
samplerBindingOverride.binding, samplerBindingOverride.texture->GetExternalIndex());
|
||||
MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout),
|
||||
"ResolveSamplerDescriptorOverride: invalid layout %d for binding %u textureId=%d",
|
||||
static_cast<Int>(resource->layout), samplerBindingOverride.binding,
|
||||
samplerBindingOverride.texture->GetExternalIndex());
|
||||
|
||||
outImageInfo = {
|
||||
.sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler),
|
||||
@@ -239,23 +264,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) const {
|
||||
outTexture.reset();
|
||||
if (!MG_State::pGLContext || binding >= programObj.samplerUniformLocationByBinding.size()) {
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveSamplerTexture: GL context is null");
|
||||
MOBILEGL_ASSERT(binding < programObj.samplerUniformLocationByBinding.size(),
|
||||
"ResolveSamplerTexture: sampler location binding %u out of range", binding);
|
||||
MOBILEGL_ASSERT(binding < programObj.samplerTextureTargetByBinding.size(),
|
||||
"ResolveSamplerTexture: sampler target binding %u out of range", binding);
|
||||
|
||||
const Int location = programObj.samplerUniformLocationByBinding[binding];
|
||||
if (location < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
if (unit < 0) {
|
||||
return false;
|
||||
}
|
||||
const Int unit = ResolveSamplerUnitIndex(program, location, binding);
|
||||
|
||||
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
|
||||
const TextureTarget preferredTarget = programObj.samplerTextureTargetByBinding[binding];
|
||||
outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject();
|
||||
MOBILEGL_ASSERT(outTexture != nullptr,
|
||||
"ResolveSamplerTexture: no texture bound for sampler binding=%u location=%d unit=%d target=%d",
|
||||
binding, location, unit, static_cast<Int>(preferredTarget));
|
||||
return outTexture != nullptr;
|
||||
}
|
||||
|
||||
@@ -284,62 +307,85 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformManager::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program,
|
||||
Vector<const void*>& outData,
|
||||
Vector<VkDeviceSize>& outSizes) const {
|
||||
outData.assign(m_maxBindings, nullptr);
|
||||
outSizes.assign(m_maxBindings, 0);
|
||||
Bool UniformManager::ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
const void*& outData, VkDeviceSize& outSize) const {
|
||||
outData = nullptr;
|
||||
outSize = 0;
|
||||
|
||||
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
|
||||
const Uint32 uniformBindingPointCount =
|
||||
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
|
||||
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveUniformBufferPayload: GL context is null");
|
||||
MOBILEGL_ASSERT(binding < programObj.bindingKinds.size(),
|
||||
"ResolveUniformBufferPayload: binding %u out of range", binding);
|
||||
MOBILEGL_ASSERT(programObj.bindingKinds[binding] == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic,
|
||||
"ResolveUniformBufferPayload: binding %u is not a uniform buffer descriptor", binding);
|
||||
|
||||
for (Uint32 blockIndex = 0; blockIndex < activeUniformBlockCount; ++blockIndex) {
|
||||
const Uint32 binding = program.GetUniformBlockBinding(blockIndex);
|
||||
if (binding >= m_maxBindings) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(blockIndex));
|
||||
if (blockSize == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding >= uniformBindingPointCount) {
|
||||
continue;
|
||||
}
|
||||
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);
|
||||
const auto bufferObject = bindingPoint.GetBoundObject();
|
||||
if (!bufferObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto bufferData = bufferObject->GetDataReadOnly();
|
||||
if (!bufferData || bufferData->empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto range = bindingPoint.GetRange();
|
||||
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
|
||||
VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
|
||||
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
|
||||
|
||||
if (rangeStart >= bufferSize) {
|
||||
continue;
|
||||
}
|
||||
if (rangeEnd <= rangeStart || rangeEnd > bufferSize) {
|
||||
rangeEnd = bufferSize;
|
||||
}
|
||||
|
||||
VkDeviceSize available = rangeEnd - rangeStart;
|
||||
if (available == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outData[binding] = bufferData->data() + static_cast<SizeT>(rangeStart);
|
||||
outSizes[binding] = std::min(blockSize, available);
|
||||
if (programObj.globalUboBinding == static_cast<Int>(binding)) {
|
||||
outData = program.GetUBOData();
|
||||
outSize = static_cast<VkDeviceSize>(program.GetUBOSize());
|
||||
MOBILEGL_ASSERT(outData != nullptr, "ResolveUniformBufferPayload: global UBO data is null");
|
||||
MOBILEGL_ASSERT(outSize > 0, "ResolveUniformBufferPayload: global UBO size is zero");
|
||||
return outData != nullptr && outSize > 0;
|
||||
}
|
||||
|
||||
MOBILEGL_ASSERT(binding < programObj.uniformBlockIndexByBinding.size(),
|
||||
"ResolveUniformBufferPayload: UBO mapping binding %u out of range", binding);
|
||||
const Int blockIndex = programObj.uniformBlockIndexByBinding[binding];
|
||||
MOBILEGL_ASSERT(blockIndex >= 0,
|
||||
"ResolveUniformBufferPayload: no uniform block mapped to descriptor binding %u", binding);
|
||||
|
||||
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
|
||||
MOBILEGL_ASSERT(static_cast<Uint32>(blockIndex) < activeUniformBlockCount,
|
||||
"ResolveUniformBufferPayload: uniform block index %d out of range (count=%u)", blockIndex,
|
||||
activeUniformBlockCount);
|
||||
|
||||
const Uint32 frontendBinding = program.GetUniformBlockBinding(static_cast<Uint32>(blockIndex));
|
||||
const Uint32 uniformBindingPointCount =
|
||||
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
|
||||
MOBILEGL_ASSERT(frontendBinding < uniformBindingPointCount,
|
||||
"ResolveUniformBufferPayload: frontend UBO binding %u out of range for block '%s'",
|
||||
frontendBinding, program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, frontendBinding);
|
||||
const auto bufferObject = bindingPoint.GetBoundObject();
|
||||
MOBILEGL_ASSERT(bufferObject != nullptr,
|
||||
"ResolveUniformBufferPayload: no UBO bound at frontend binding %u for block '%s'",
|
||||
frontendBinding, program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
const auto bufferData = bufferObject->GetDataReadOnly();
|
||||
MOBILEGL_ASSERT(bufferData != nullptr && !bufferData->empty(),
|
||||
"ResolveUniformBufferPayload: bound UBO data is empty for block '%s'",
|
||||
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
const auto range = bindingPoint.GetRange();
|
||||
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
|
||||
const VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
|
||||
MOBILEGL_ASSERT(rangeStart < bufferSize,
|
||||
"ResolveUniformBufferPayload: UBO range start %zu exceeds buffer size %zu for block '%s'",
|
||||
static_cast<SizeT>(rangeStart), static_cast<SizeT>(bufferSize),
|
||||
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
|
||||
if (rangeEnd > bufferSize) {
|
||||
rangeEnd = bufferSize;
|
||||
}
|
||||
MOBILEGL_ASSERT(rangeEnd > rangeStart,
|
||||
"ResolveUniformBufferPayload: invalid UBO range [%zu, %zu) for block '%s'",
|
||||
static_cast<SizeT>(rangeStart), static_cast<SizeT>(rangeEnd),
|
||||
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
const VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(static_cast<Uint32>(blockIndex)));
|
||||
MOBILEGL_ASSERT(blockSize > 0,
|
||||
"ResolveUniformBufferPayload: reflected UBO size is zero for block '%s'",
|
||||
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
const VkDeviceSize available = rangeEnd - rangeStart;
|
||||
MOBILEGL_ASSERT(available >= blockSize,
|
||||
"ResolveUniformBufferPayload: bound range %zu is smaller than UBO size %zu for block '%s'",
|
||||
static_cast<SizeT>(available), static_cast<SizeT>(blockSize),
|
||||
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
|
||||
outData = bufferData->data() + static_cast<SizeT>(rangeStart);
|
||||
outSize = blockSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -450,13 +496,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<const void*> bindingData;
|
||||
Vector<VkDeviceSize> bindingSizes;
|
||||
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot gather UBO payloads");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null");
|
||||
MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null");
|
||||
MOBILEGL_ASSERT(m_bufferManager != nullptr, "BindProgramUniformBuffers: buffer manager is null");
|
||||
@@ -486,18 +525,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
write.descriptorCount = 1;
|
||||
|
||||
if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic) {
|
||||
const void* payload = bindingData[binding];
|
||||
VkDeviceSize payloadSize = bindingSizes[binding];
|
||||
if (payload == nullptr || payloadSize == 0) {
|
||||
if (programObj.globalUboBinding == static_cast<Int>(binding)) {
|
||||
const void* globalUboData = program.GetUBOData();
|
||||
const VkDeviceSize globalUboSize = static_cast<VkDeviceSize>(program.GetUBOSize());
|
||||
if (globalUboData != nullptr && globalUboSize > 0) {
|
||||
payload = globalUboData;
|
||||
payloadSize = globalUboSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
const void* payload = nullptr;
|
||||
VkDeviceSize payloadSize = 0;
|
||||
const Bool hasPayload = ResolveUniformBufferPayload(program, programObj, binding, payload, payloadSize);
|
||||
MOBILEGL_ASSERT(hasPayload && payload != nullptr && payloadSize > 0,
|
||||
"UniformDescriptorBinder::BindProgramUniformBuffers failed: missing UBO payload on binding %u",
|
||||
binding);
|
||||
|
||||
BufferSlice slice{};
|
||||
if (!m_bufferManager->UploadTransient(BufferKind::Uniform, frameIndex, payload, payloadSize,
|
||||
@@ -528,16 +561,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
} else {
|
||||
hasImage = ResolveSamplerDescriptor(commandBuffer, program, programObj, binding, imageInfo);
|
||||
}
|
||||
if (!hasImage) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor",
|
||||
binding);
|
||||
return false;
|
||||
}
|
||||
if (imageInfo.sampler == VK_NULL_HANDLE || imageInfo.imageView == VK_NULL_HANDLE) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has null sampler or imageView",
|
||||
binding);
|
||||
return false;
|
||||
}
|
||||
MOBILEGL_ASSERT(hasImage,
|
||||
"UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor",
|
||||
binding);
|
||||
MOBILEGL_ASSERT(imageInfo.sampler != VK_NULL_HANDLE && imageInfo.imageView != VK_NULL_HANDLE,
|
||||
"UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has null sampler or imageView",
|
||||
binding);
|
||||
imageInfos.push_back(imageInfo);
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imageInfos.back();
|
||||
|
||||
@@ -69,8 +69,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDescriptorImageInfo& outImageInfo) const;
|
||||
Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride,
|
||||
VkDescriptorImageInfo& outImageInfo) const;
|
||||
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
|
||||
Vector<VkDeviceSize>& outSizes) const;
|
||||
Bool ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
|
||||
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
|
||||
const void*& outData, VkDeviceSize& outSize) const;
|
||||
Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const;
|
||||
Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex);
|
||||
VkResult AllocateDescriptorSetsFromActivePool(
|
||||
|
||||
@@ -200,7 +200,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
auto* texture2d =
|
||||
static_cast<MG_State::GLState::TextureObject2D*>(texture);
|
||||
desc.flags = 0;
|
||||
desc.format =
|
||||
desc.format = isDefaultFbo ?
|
||||
m_swapchainObject.GetSurfaceFormat().format :
|
||||
MG_Util::ConvertTextureInternalFormatToVkEnum(
|
||||
texture2d->GetFormat());
|
||||
desc.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
@@ -242,6 +243,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
textureResources[i] = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||
MOBILEGL_ASSERT(textureResources[i],
|
||||
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
|
||||
desc.format = textureResources[i]->format;
|
||||
trackedColorLayout = textureResources[i]->layout;
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::Texture,
|
||||
@@ -300,8 +302,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
trackedDepthLayout = depthTextureResource->layout;
|
||||
}
|
||||
depthAttachmentDescription.flags = 0;
|
||||
depthAttachmentDescription.format =
|
||||
depthAttachmentDescription.format = isDefaultFbo ?
|
||||
m_swapchainObject.GetDepthStencilFormat() :
|
||||
MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
|
||||
if (!isDefaultFbo) {
|
||||
depthAttachmentDescription.format = depthTextureResource->format;
|
||||
}
|
||||
depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
depthAttachmentDescription.loadOp = clearDepth ?
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
@@ -409,8 +415,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Move(pendingClearAttachments),
|
||||
Move(trackedAttachmentLayouts),
|
||||
static_cast<Uint32>(attachmentViews.size()),
|
||||
static_cast<Uint32>(colorAttachmentRefs.size()),
|
||||
extent,
|
||||
1 };
|
||||
MGLOG_D("VkRenderPassManager::GetOrCreateRenderPass: hash=0x%llx compatibilityHash=0x%llx attachmentCount=%u colorAttachmentCount=%u extent=%dx%d",
|
||||
static_cast<unsigned long long>(hash),
|
||||
static_cast<unsigned long long>(compatibilityHash),
|
||||
renderPassEntry.attachmentCount,
|
||||
renderPassEntry.colorAttachmentCount,
|
||||
extent.x(),
|
||||
extent.y());
|
||||
auto [insertedIt, _] = m_renderPasses.emplace(hash, Move(renderPassEntry));
|
||||
return insertedIt->second;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<PendingClearAttachmentInfo> pendingClearAttachments;
|
||||
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
|
||||
Uint32 attachmentCount = 0;
|
||||
Uint32 colorAttachmentCount = 0;
|
||||
IntVec2 extent = {0, 0};
|
||||
Uint32 subpass = 0;
|
||||
|
||||
@@ -59,6 +60,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
std::swap(pendingClearAttachments, that.pendingClearAttachments);
|
||||
std::swap(trackedAttachmentLayouts, that.trackedAttachmentLayouts);
|
||||
std::swap(attachmentCount, that.attachmentCount);
|
||||
std::swap(colorAttachmentCount, that.colorAttachmentCount);
|
||||
std::swap(extent, that.extent);
|
||||
std::swap(subpass, that.subpass);
|
||||
}
|
||||
@@ -70,6 +72,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const Vector<PendingClearAttachmentInfo>& pendingClearAttachments,
|
||||
const Vector<TrackedAttachmentLayoutInfo>& trackedAttachmentLayouts,
|
||||
Uint32 attachmentCount,
|
||||
Uint32 colorAttachmentCount,
|
||||
IntVec2 extent, int subpass):
|
||||
hash(hash),
|
||||
renderPass(renderpass),
|
||||
@@ -78,6 +81,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
pendingClearAttachments(Move(pendingClearAttachments)),
|
||||
trackedAttachmentLayouts(Move(trackedAttachmentLayouts)),
|
||||
attachmentCount(attachmentCount),
|
||||
colorAttachmentCount(colorAttachmentCount),
|
||||
extent(extent),
|
||||
subpass(subpass)
|
||||
{}
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static constexpr VkPipelineStageFlags kGraphicsSampledReadStages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
|
||||
struct TextureFormatInfo {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
Bool expandRgbToRgba = false;
|
||||
Uint32 componentByteCount = 0;
|
||||
Array<Uint8, 4> alphaBytes = {0, 0, 0, 0};
|
||||
};
|
||||
|
||||
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
||||
switch (layout) {
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
@@ -27,6 +34,82 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
static TextureFormatInfo ResolveTextureFormatInfo(TextureInternalFormat format) {
|
||||
switch (format) {
|
||||
case TextureInternalFormat::RGB:
|
||||
case TextureInternalFormat::RGB8:
|
||||
return {VK_FORMAT_R8G8B8A8_UNORM, true, 1, {0xFF, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::SRGB8:
|
||||
return {VK_FORMAT_R8G8B8A8_SRGB, true, 1, {0xFF, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB8Snorm:
|
||||
return {VK_FORMAT_R8G8B8A8_SNORM, true, 1, {0x7F, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB16:
|
||||
return {VK_FORMAT_R16G16B16A16_UNORM, true, 2, {0xFF, 0xFF, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB16Snorm:
|
||||
return {VK_FORMAT_R16G16B16A16_SNORM, true, 2, {0xFF, 0x7F, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB16F:
|
||||
return {VK_FORMAT_R16G16B16A16_SFLOAT, true, 2, {0x00, 0x3C, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB32F:
|
||||
return {VK_FORMAT_R32G32B32A32_SFLOAT, true, 4, {0x00, 0x00, 0x80, 0x3F}};
|
||||
case TextureInternalFormat::RGB8I:
|
||||
return {VK_FORMAT_R8G8B8A8_SINT, true, 1, {0x01, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB8UI:
|
||||
return {VK_FORMAT_R8G8B8A8_UINT, true, 1, {0x01, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB16I:
|
||||
return {VK_FORMAT_R16G16B16A16_SINT, true, 2, {0x01, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB16UI:
|
||||
return {VK_FORMAT_R16G16B16A16_UINT, true, 2, {0x01, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB32I:
|
||||
return {VK_FORMAT_R32G32B32A32_SINT, true, 4, {0x01, 0x00, 0x00, 0x00}};
|
||||
case TextureInternalFormat::RGB32UI:
|
||||
return {VK_FORMAT_R32G32B32A32_UINT, true, 4, {0x01, 0x00, 0x00, 0x00}};
|
||||
default:
|
||||
return {MG_Util::ConvertTextureInternalFormatToVkEnum(format), false, 0, {0, 0, 0, 0}};
|
||||
}
|
||||
}
|
||||
|
||||
static Bool ExpandRgbSourceToRgba(const void* source, SizeT sourceByteSize, const IntVec3& texelSize,
|
||||
const TextureFormatInfo& formatInfo, Vector<Uint8>& outExpandedData) {
|
||||
MOBILEGL_ASSERT(source != nullptr, "ExpandRgbSourceToRgba: source is null");
|
||||
MOBILEGL_ASSERT(formatInfo.expandRgbToRgba, "ExpandRgbSourceToRgba: format does not require RGB expansion");
|
||||
MOBILEGL_ASSERT(formatInfo.componentByteCount > 0,
|
||||
"ExpandRgbSourceToRgba: invalid component size for expanded RGB format");
|
||||
|
||||
const SizeT depth = static_cast<SizeT>(std::max(texelSize.z(), 1));
|
||||
const SizeT pixelCount = static_cast<SizeT>(texelSize.x()) * static_cast<SizeT>(texelSize.y()) * depth;
|
||||
MOBILEGL_ASSERT(pixelCount > 0, "ExpandRgbSourceToRgba: invalid texel size (%d, %d, %d)",
|
||||
texelSize.x(), texelSize.y(), texelSize.z());
|
||||
MOBILEGL_ASSERT(sourceByteSize == pixelCount * formatInfo.componentByteCount * 3,
|
||||
"ExpandRgbSourceToRgba: unexpected source byte size=%zu for pixelCount=%zu componentBytes=%u",
|
||||
sourceByteSize, pixelCount, formatInfo.componentByteCount);
|
||||
|
||||
outExpandedData.resize(pixelCount * formatInfo.componentByteCount * 4);
|
||||
const auto* src = static_cast<const Uint8*>(source);
|
||||
auto* dst = outExpandedData.data();
|
||||
const SizeT srcPixelSize = static_cast<SizeT>(formatInfo.componentByteCount) * 3;
|
||||
const SizeT dstPixelSize = static_cast<SizeT>(formatInfo.componentByteCount) * 4;
|
||||
for (SizeT pixel = 0; pixel < pixelCount; ++pixel) {
|
||||
const SizeT srcOffset = pixel * srcPixelSize;
|
||||
const SizeT dstOffset = pixel * dstPixelSize;
|
||||
std::memcpy(dst + dstOffset, src + srcOffset, srcPixelSize);
|
||||
std::memcpy(dst + dstOffset + srcPixelSize, formatInfo.alphaBytes.data(), formatInfo.componentByteCount);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static VkComponentMapping ResolveSampledViewComponents(const MG_State::GLState::ITextureObject& texture) {
|
||||
VkComponentMapping components{
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A,
|
||||
};
|
||||
if (ResolveTextureFormatInfo(texture.GetFormat()).expandRgbToRgba) {
|
||||
components.a = VK_COMPONENT_SWIZZLE_ONE;
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
Bool VkTextureManager::Initialize(const InitInfo& initInfo) {
|
||||
Shutdown();
|
||||
|
||||
@@ -256,7 +339,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
TextureUploadTarget uploadTarget,
|
||||
const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels,
|
||||
TextureResource &resource) {
|
||||
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat());
|
||||
const VkFormat format = formatInfo.format;
|
||||
if (format == VK_FORMAT_UNDEFINED) {
|
||||
MGLOG_D("%s: format == VK_FORMAT_UNDEFINED", __func__);
|
||||
return false;
|
||||
@@ -347,7 +431,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
resource.fullView = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, baseMipLevel, levelCount);
|
||||
const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture);
|
||||
resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, baseMipLevel, levelCount,
|
||||
&sampledComponents);
|
||||
if (resource.fullView == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
@@ -359,12 +445,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
VkImageView VkTextureManager::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
|
||||
Uint32 baseMipLevel, Uint32 levelCount) const {
|
||||
Uint32 baseMipLevel, Uint32 levelCount,
|
||||
const VkComponentMapping* components) const {
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = format;
|
||||
viewInfo.components = components != nullptr ?
|
||||
*components :
|
||||
VkComponentMapping{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
|
||||
viewInfo.subresourceRange.aspectMask = aspect;
|
||||
viewInfo.subresourceRange.baseMipLevel = baseMipLevel;
|
||||
viewInfo.subresourceRange.levelCount = levelCount;
|
||||
@@ -381,14 +472,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
TextureResource &outResource) {
|
||||
struct UploadItem {
|
||||
Uint32 level = 0;
|
||||
SizeT byteSize = 0;
|
||||
SizeT uploadByteSize = 0;
|
||||
IntVec3 texelSize = {0, 0, 0};
|
||||
const void* source = nullptr;
|
||||
Vector<Uint8> expandedData;
|
||||
VkDeviceSize offset = 0;
|
||||
};
|
||||
|
||||
Vector<UploadItem> uploadItems;
|
||||
uploadItems.reserve(outResource.mipLevels);
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(mipmapTexture.GetFormat());
|
||||
|
||||
VkDeviceSize stagingSize = 0;
|
||||
for (Uint32 level = 0; level < outResource.mipLevels; ++level) {
|
||||
@@ -409,8 +502,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
uploadItems.push_back({level, byteSize, texelSize, source, stagingSize});
|
||||
stagingSize += static_cast<VkDeviceSize>(byteSize);
|
||||
UploadItem uploadItem{};
|
||||
uploadItem.level = level;
|
||||
uploadItem.texelSize = texelSize;
|
||||
uploadItem.source = source;
|
||||
uploadItem.offset = stagingSize;
|
||||
uploadItem.uploadByteSize = byteSize;
|
||||
if (formatInfo.expandRgbToRgba) {
|
||||
const Bool expanded = ExpandRgbSourceToRgba(source, byteSize, texelSize, formatInfo,
|
||||
uploadItem.expandedData);
|
||||
MOBILEGL_ASSERT(expanded,
|
||||
"UploadDirtyMipLevels: failed to expand RGB textureId=%d level=%u to RGBA staging data",
|
||||
mipmapTexture.GetExternalIndex(), level);
|
||||
uploadItem.uploadByteSize = uploadItem.expandedData.size();
|
||||
}
|
||||
uploadItems.push_back(Move(uploadItem));
|
||||
if (!uploadItems.back().expandedData.empty()) {
|
||||
uploadItems.back().source = uploadItems.back().expandedData.data();
|
||||
}
|
||||
stagingSize += static_cast<VkDeviceSize>(uploadItems.back().uploadByteSize);
|
||||
}
|
||||
|
||||
if (uploadItems.empty()) {
|
||||
@@ -435,7 +545,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void* mapped = nullptr;
|
||||
VK_VERIFY(vmaMapMemory(m_allocator, stagingAllocation, &mapped), "vmaMapMemory(staging texture)");
|
||||
for (const auto& item : uploadItems) {
|
||||
std::memcpy(static_cast<Uint8*>(mapped) + item.offset, item.source, item.byteSize);
|
||||
std::memcpy(static_cast<Uint8*>(mapped) + item.offset, item.source, item.uploadByteSize);
|
||||
}
|
||||
vmaUnmapMemory(m_allocator, stagingAllocation);
|
||||
|
||||
|
||||
@@ -119,7 +119,8 @@ private:
|
||||
TextureResource &resource);
|
||||
Bool SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource);
|
||||
VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
|
||||
Uint32 baseMipLevel, Uint32 levelCount) const;
|
||||
Uint32 baseMipLevel, Uint32 levelCount,
|
||||
const VkComponentMapping* components = nullptr) const;
|
||||
Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
|
||||
TextureUploadTarget uploadTarget,
|
||||
TextureResource &outResource);
|
||||
|
||||
@@ -635,6 +635,23 @@ void main() {
|
||||
"InitializeBlitResources: missing uSurfaceTransform");
|
||||
MOBILEGL_ASSERT(m_blitResources.program->GetUBOSize() > 0,
|
||||
"InitializeBlitResources: blit program global UBO is empty");
|
||||
MOBILEGL_ASSERT(m_programFactory != nullptr, "InitializeBlitResources: program factory is null");
|
||||
|
||||
ProgramFactory::CompileOptionFlags blitTransformFlags = 0;
|
||||
const auto& blitProgramObj = m_programFactory->GetOrCreateProgram(*m_blitResources.program, blitTransformFlags);
|
||||
Bool foundBlitSamplerBinding = false;
|
||||
for (Uint32 binding = 0; binding < blitProgramObj.samplerNameByBinding.size(); ++binding) {
|
||||
if (blitProgramObj.bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) {
|
||||
continue;
|
||||
}
|
||||
if (blitProgramObj.samplerNameByBinding[binding] == "uSource") {
|
||||
m_blitResources.samplerBinding = binding;
|
||||
foundBlitSamplerBinding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOBILEGL_ASSERT(foundBlitSamplerBinding,
|
||||
"InitializeBlitResources: failed to resolve reflected binding for uSource");
|
||||
|
||||
auto createSampler = [](Uint externalIndex, SamplerFilterMode filter) {
|
||||
auto sampler = MakeShared<MG_State::GLState::SamplerObject>(externalIndex);
|
||||
@@ -650,7 +667,6 @@ void main() {
|
||||
|
||||
m_blitResources.nearestSampler = createSampler(kHiddenBlitNearestSamplerId, SamplerFilterMode::Nearest);
|
||||
m_blitResources.linearSampler = createSampler(kHiddenBlitLinearSamplerId, SamplerFilterMode::Linear);
|
||||
m_blitResources.samplerBinding = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -673,6 +689,7 @@ void main() {
|
||||
.vertexInputHash = 0,
|
||||
.pipelineLayout = programObj.pipelineLayout,
|
||||
.renderPass = renderPassEntry.renderPass,
|
||||
.colorAttachmentCount = renderPassEntry.colorAttachmentCount,
|
||||
.subpass = 0,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
@@ -722,6 +739,7 @@ void main() {
|
||||
.vertexInputHash = vertexInputHash,
|
||||
.pipelineLayout = programObj.pipelineLayout,
|
||||
.renderPass = renderPassEntry.renderPass,
|
||||
.colorAttachmentCount = renderPassEntry.colorAttachmentCount,
|
||||
.subpass = 0,
|
||||
.topology = MG_Util::ConvertPrimitiveModeToVkEnum(mode),
|
||||
.cullMode = cullFaceEnabled
|
||||
@@ -856,8 +874,12 @@ void main() {
|
||||
|
||||
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
|
||||
m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj,
|
||||
m_frameContext.GetCurrentFrameIndex());
|
||||
const Bool boundUniforms = m_uniformManager->BindProgramUniformBuffers(
|
||||
frame.commandBuffer, program, programObj, m_frameContext.GetCurrentFrameIndex());
|
||||
if (!boundUniforms) {
|
||||
MGLOG_E("SetupDraw skipped: BindProgramUniformBuffers failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto vtxUploadOk = UploadAndBindVertexBuffers(frame.commandBuffer, vao);
|
||||
MOBILEGL_ASSERT(vtxUploadOk, "SetupDraw skipped: failed to upload vertex buffers");
|
||||
|
||||
Reference in New Issue
Block a user