mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
[Feat] (MG_Backend/DirectVulkan): initial implementation of VkTextureSamplerManager, only fallback path available now
This commit is contained in:
@@ -220,6 +220,7 @@ set(SOURCE_FILES
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp
|
||||
|
||||
MobileGL/MG_State/GLState/Core.cpp
|
||||
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
||||
|
||||
@@ -20,9 +20,72 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return (value + alignment - 1) / alignment * alignment;
|
||||
}
|
||||
|
||||
Uint64 UniformDescriptorBinder::ComputeProgramHash(const MG_State::GLState::ProgramObject& program) {
|
||||
XXH64_state_t* state = XXH64_createState();
|
||||
XXHASH_VERIFY(XXH64_reset(state, 0xC0D3A11ULL));
|
||||
const auto& spirv = program.GetGeneratedSpirv();
|
||||
for (const auto& module : spirv) {
|
||||
XXHASH_VERIFY(XXH64_update(state, module.data(), module.size() * sizeof(Uint)));
|
||||
}
|
||||
const Uint32 blockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
|
||||
XXHASH_VERIFY(XXH64_update(state, &blockCount, sizeof(blockCount)));
|
||||
for (Uint32 i = 0; i < blockCount; ++i) {
|
||||
const Uint32 binding = program.GetUniformBlockBinding(i);
|
||||
XXHASH_VERIFY(XXH64_update(state, &binding, sizeof(binding)));
|
||||
}
|
||||
const Uint64 hash = XXH64_digest(state);
|
||||
XXH64_freeState(state);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::IsSamplerUniformType(GLenum glType) {
|
||||
switch (glType) {
|
||||
case GL_SAMPLER_1D:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_1D_SHADOW:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_1D_ARRAY:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_1D_ARRAY_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_BUFFER:
|
||||
case GL_SAMPLER_2D_RECT:
|
||||
case GL_SAMPLER_2D_RECT_SHADOW:
|
||||
case GL_INT_SAMPLER_1D:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_1D_ARRAY:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
case GL_INT_SAMPLER_2D_RECT:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator,
|
||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes) {
|
||||
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes,
|
||||
VkTextureSamplerManager* textureSamplerManager) {
|
||||
Shutdown();
|
||||
|
||||
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
|
||||
@@ -38,53 +101,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_frameCount = frameCount;
|
||||
m_maxBindings = maxBindings;
|
||||
m_setsPerFrame = setsPerFrame;
|
||||
|
||||
Vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(m_maxBindings);
|
||||
for (Uint32 bindingIndex = 0; bindingIndex < m_maxBindings; ++bindingIndex) {
|
||||
VkDescriptorSetLayoutBinding binding{};
|
||||
binding.binding = bindingIndex;
|
||||
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
binding.descriptorCount = 1;
|
||||
binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||
binding.pImmutableSamplers = nullptr;
|
||||
bindings.push_back(binding);
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
|
||||
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
setLayoutInfo.bindingCount = static_cast<Uint32>(bindings.size());
|
||||
setLayoutInfo.pBindings = bindings.data();
|
||||
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &m_descriptorSetLayout),
|
||||
"UniformDescriptorBinder::Initialize, vkCreateDescriptorSetLayout");
|
||||
|
||||
VkDescriptorPoolSize poolSize{};
|
||||
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
poolSize.descriptorCount = m_frameCount * m_setsPerFrame * m_maxBindings;
|
||||
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.maxSets = m_frameCount * m_setsPerFrame;
|
||||
poolInfo.poolSizeCount = 1;
|
||||
poolInfo.pPoolSizes = &poolSize;
|
||||
VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool),
|
||||
"UniformDescriptorBinder::Initialize, vkCreateDescriptorPool");
|
||||
|
||||
m_descriptorSets.resize(m_frameCount * m_setsPerFrame, VK_NULL_HANDLE);
|
||||
Vector<VkDescriptorSetLayout> layouts(m_descriptorSets.size(), m_descriptorSetLayout);
|
||||
VkDescriptorSetAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
allocInfo.descriptorPool = m_descriptorPool;
|
||||
allocInfo.descriptorSetCount = static_cast<Uint32>(layouts.size());
|
||||
allocInfo.pSetLayouts = layouts.data();
|
||||
VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, m_descriptorSets.data()),
|
||||
"UniformDescriptorBinder::Initialize, vkAllocateDescriptorSets");
|
||||
m_textureSamplerManager = textureSamplerManager;
|
||||
|
||||
m_frames.resize(m_frameCount);
|
||||
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
|
||||
auto& frame = m_frames[frameIndex];
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
frame.descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorPoolSize poolSizes[2]{};
|
||||
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
poolSizes[0].descriptorCount = m_setsPerFrame * m_maxBindings;
|
||||
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
poolSizes[1].descriptorCount = m_setsPerFrame * m_maxBindings;
|
||||
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.maxSets = m_setsPerFrame;
|
||||
poolInfo.poolSizeCount = static_cast<Uint32>(std::size(poolSizes));
|
||||
poolInfo.pPoolSizes = poolSizes;
|
||||
VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &frame.descriptorPool),
|
||||
"UniformDescriptorBinder::Initialize, vkCreateDescriptorPool(frame)");
|
||||
|
||||
const Bool created = frame.uploadBuffer.Create(
|
||||
m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
|
||||
@@ -101,21 +139,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void UniformDescriptorBinder::Shutdown() {
|
||||
for (auto& frame : m_frames) {
|
||||
frame.uploadBuffer.Destroy();
|
||||
if (m_device != VK_NULL_HANDLE && frame.descriptorPool != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorPool(m_device, frame.descriptorPool, nullptr);
|
||||
}
|
||||
frame.descriptorPool = VK_NULL_HANDLE;
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
}
|
||||
m_frames.clear();
|
||||
m_descriptorSets.clear();
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_descriptorPool != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);
|
||||
}
|
||||
m_descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_descriptorSetLayout != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr);
|
||||
}
|
||||
m_descriptorSetLayout = VK_NULL_HANDLE;
|
||||
DestroyProgramLayouts();
|
||||
|
||||
m_allocator = nullptr;
|
||||
m_device = VK_NULL_HANDLE;
|
||||
@@ -124,13 +155,141 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_frameCount = 0;
|
||||
m_maxBindings = 0;
|
||||
m_setsPerFrame = 0;
|
||||
m_textureSamplerManager = nullptr;
|
||||
}
|
||||
|
||||
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
||||
auto& frame = m_frames[frameIndex];
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
VK_VERIFY(vkResetDescriptorPool(m_device, frame.descriptorPool, 0),
|
||||
"UniformDescriptorBinder::BeginFrame, vkResetDescriptorPool");
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::ReflectBindingKinds(const MG_State::GLState::ProgramObject& program,
|
||||
Vector<BindingKind>& outKinds) const {
|
||||
outKinds.assign(m_maxBindings, BindingKind::None);
|
||||
|
||||
const auto& spirv = program.GetGeneratedSpirv();
|
||||
for (const auto& module : spirv) {
|
||||
if (module.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spvc_context context = nullptr;
|
||||
spvc_parsed_ir ir = nullptr;
|
||||
spvc_compiler compiler = nullptr;
|
||||
spvc_resources resources = nullptr;
|
||||
|
||||
if (spvc_context_create(&context) != SPVC_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const spvc_result parseResult = spvc_context_parse_spirv(context, module.data(), module.size(), &ir);
|
||||
if (parseResult != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
|
||||
const spvc_result compilerResult =
|
||||
spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler);
|
||||
if (compilerResult != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto applyBindings = [&](spvc_resource_type resourceType, BindingKind kind) {
|
||||
const spvc_reflected_resource* list = nullptr;
|
||||
size_t count = 0;
|
||||
if (spvc_resources_get_resource_list_for_type(resources, resourceType, &list, &count) != SPVC_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const Uint32 binding =
|
||||
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding);
|
||||
if (binding >= m_maxBindings) {
|
||||
continue;
|
||||
}
|
||||
if (kind == BindingKind::CombinedImageSampler) {
|
||||
outKinds[binding] = BindingKind::CombinedImageSampler;
|
||||
} else if (outKinds[binding] == BindingKind::None) {
|
||||
outKinds[binding] = kind;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
applyBindings(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, BindingKind::UniformBufferDynamic);
|
||||
applyBindings(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, BindingKind::CombinedImageSampler);
|
||||
|
||||
spvc_context_destroy(context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout(
|
||||
const MG_State::GLState::ProgramObject& program) {
|
||||
const Uint64 hash = ComputeProgramHash(program);
|
||||
auto it = m_programLayouts.find(hash);
|
||||
if (it != m_programLayouts.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
ProgramLayout layout{};
|
||||
layout.hash = hash;
|
||||
if (!ReflectBindingKinds(program, layout.bindingKinds)) {
|
||||
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: reflection failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(m_maxBindings);
|
||||
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
|
||||
const auto kind = layout.bindingKinds[binding];
|
||||
if (kind == BindingKind::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding layoutBinding{};
|
||||
layoutBinding.binding = binding;
|
||||
layoutBinding.descriptorCount = 1;
|
||||
layoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||
layoutBinding.pImmutableSamplers = nullptr;
|
||||
if (kind == BindingKind::UniformBufferDynamic) {
|
||||
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
layout.dynamicBindings.push_back(binding);
|
||||
} else {
|
||||
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
}
|
||||
bindings.push_back(layoutBinding);
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
|
||||
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
setLayoutInfo.bindingCount = static_cast<Uint32>(bindings.size());
|
||||
setLayoutInfo.pBindings = bindings.data();
|
||||
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &layout.descriptorSetLayout),
|
||||
"UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreateDescriptorSetLayout");
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 1;
|
||||
pipelineLayoutInfo.pSetLayouts = &layout.descriptorSetLayout;
|
||||
VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &layout.pipelineLayout),
|
||||
"UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreatePipelineLayout");
|
||||
|
||||
auto [insertIt, _] = m_programLayouts.emplace(hash, std::move(layout));
|
||||
return &insertIt->second;
|
||||
}
|
||||
|
||||
VkPipelineLayout UniformDescriptorBinder::GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program) {
|
||||
auto* layout = GetOrCreateProgramLayout(program);
|
||||
return layout ? layout->pipelineLayout : VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset) {
|
||||
@@ -221,7 +380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
Uint32 frameIndex) {
|
||||
if (m_descriptorSetLayout == VK_NULL_HANDLE || m_descriptorPool == VK_NULL_HANDLE) {
|
||||
if (m_frames.empty()) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized");
|
||||
return false;
|
||||
}
|
||||
@@ -230,16 +389,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& frame = m_frames[frameIndex];
|
||||
if (frame.descriptorCursor >= m_setsPerFrame) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: per-frame descriptor set budget exceeded");
|
||||
ProgramLayout* layout = GetOrCreateProgramLayout(program);
|
||||
if (!layout || layout->pipelineLayout == VK_NULL_HANDLE || layout->descriptorSetLayout == VK_NULL_HANDLE) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot get program layout");
|
||||
return false;
|
||||
}
|
||||
if (layout->pipelineLayout != pipelineLayout) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: pipelineLayout mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
const Uint32 descriptorSetIndex = frameIndex * m_setsPerFrame + frame.descriptorCursor;
|
||||
const VkDescriptorSet descriptorSet = m_descriptorSets[descriptorSetIndex];
|
||||
++frame.descriptorCursor;
|
||||
auto& frame = m_frames[frameIndex];
|
||||
if (frame.descriptorPool == VK_NULL_HANDLE) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pool is invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkDescriptorSetAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
allocInfo.descriptorPool = frame.descriptorPool;
|
||||
allocInfo.descriptorSetCount = 1;
|
||||
allocInfo.pSetLayouts = &layout->descriptorSetLayout;
|
||||
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
||||
VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, &descriptorSet),
|
||||
"UniformDescriptorBinder::BindProgramUniformBuffers, vkAllocateDescriptorSets");
|
||||
Vector<const void*> bindingData;
|
||||
Vector<VkDeviceSize> bindingSizes;
|
||||
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
|
||||
@@ -248,51 +421,92 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
static const Uint8 kFallbackData[16] = {};
|
||||
VkDescriptorImageInfo fallbackImageInfo{};
|
||||
const Bool hasFallbackImage = m_textureSamplerManager && m_textureSamplerManager->GetFallbackDescriptor(fallbackImageInfo);
|
||||
|
||||
Vector<VkDescriptorBufferInfo> bufferInfos(m_maxBindings);
|
||||
Vector<VkWriteDescriptorSet> writes;
|
||||
writes.reserve(m_maxBindings);
|
||||
Vector<Uint32> dynamicOffsets(m_maxBindings, 0);
|
||||
Vector<VkDescriptorBufferInfo> bufferInfos;
|
||||
Vector<VkDescriptorImageInfo> imageInfos;
|
||||
Vector<Uint32> dynamicOffsets;
|
||||
bufferInfos.reserve(m_maxBindings);
|
||||
imageInfos.reserve(m_maxBindings);
|
||||
dynamicOffsets.reserve(layout->dynamicBindings.size());
|
||||
|
||||
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
|
||||
const void* payload = bindingData[binding];
|
||||
VkDeviceSize payloadSize = bindingSizes[binding];
|
||||
if (payload == nullptr || payloadSize == 0) {
|
||||
payload = kFallbackData;
|
||||
payloadSize = sizeof(kFallbackData);
|
||||
const auto kind = layout->bindingKinds[binding];
|
||||
if (kind == BindingKind::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VkDeviceSize payloadOffset = 0;
|
||||
if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted");
|
||||
return false;
|
||||
}
|
||||
if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: upload failed on binding %u", binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
bufferInfos[binding].buffer = frame.uploadBuffer.GetHandle();
|
||||
bufferInfos[binding].offset = 0;
|
||||
bufferInfos[binding].range = payloadSize;
|
||||
dynamicOffsets[binding] = static_cast<Uint32>(payloadOffset);
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = descriptorSet;
|
||||
write.dstBinding = binding;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
write.pBufferInfo = &bufferInfos[binding];
|
||||
writes.push_back(write);
|
||||
|
||||
if (kind == BindingKind::UniformBufferDynamic) {
|
||||
const void* payload = bindingData[binding];
|
||||
VkDeviceSize payloadSize = bindingSizes[binding];
|
||||
if (payload == nullptr || payloadSize == 0) {
|
||||
payload = kFallbackData;
|
||||
payloadSize = sizeof(kFallbackData);
|
||||
}
|
||||
|
||||
VkDeviceSize payloadOffset = 0;
|
||||
if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted");
|
||||
return false;
|
||||
}
|
||||
if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u",
|
||||
binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkDescriptorBufferInfo bufferInfo{};
|
||||
bufferInfo.buffer = frame.uploadBuffer.GetHandle();
|
||||
bufferInfo.offset = 0;
|
||||
bufferInfo.range = payloadSize;
|
||||
bufferInfos.push_back(bufferInfo);
|
||||
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
write.pBufferInfo = &bufferInfos.back();
|
||||
writes.push_back(write);
|
||||
dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset));
|
||||
} else {
|
||||
if (!hasFallbackImage) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable");
|
||||
return false;
|
||||
}
|
||||
imageInfos.push_back(fallbackImageInfo);
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imageInfos.back();
|
||||
writes.push_back(write);
|
||||
}
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr);
|
||||
if (!writes.empty()) {
|
||||
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet,
|
||||
static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
void UniformDescriptorBinder::DestroyProgramLayouts() {
|
||||
for (auto& [_, layout] : m_programLayouts) {
|
||||
if (layout.pipelineLayout != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(m_device, layout.pipelineLayout, nullptr);
|
||||
layout.pipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
if (layout.descriptorSetLayout != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorSetLayout(m_device, layout.descriptorSetLayout, nullptr);
|
||||
layout.descriptorSetLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
m_programLayouts.clear();
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "VkBufferObject.h"
|
||||
#include "VkTextureSamplerManager.h"
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
@@ -20,42 +21,59 @@ namespace MobileGL::MG_State::GLState {
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class UniformDescriptorBinder {
|
||||
public:
|
||||
enum class BindingKind : Uint8 {
|
||||
None = 0,
|
||||
UniformBufferDynamic,
|
||||
CombinedImageSampler
|
||||
};
|
||||
|
||||
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
|
||||
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
|
||||
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024);
|
||||
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024,
|
||||
VkTextureSamplerManager* textureSamplerManager = nullptr);
|
||||
void Shutdown();
|
||||
|
||||
void BeginFrame(Uint32 frameIndex);
|
||||
VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program);
|
||||
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
|
||||
|
||||
VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; }
|
||||
|
||||
private:
|
||||
struct FrameResources {
|
||||
VkBufferObject uploadBuffer;
|
||||
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||
VkDeviceSize writeCursor = 0;
|
||||
Uint32 descriptorCursor = 0;
|
||||
};
|
||||
|
||||
struct ProgramLayout {
|
||||
Uint64 hash = 0;
|
||||
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
Vector<BindingKind> bindingKinds;
|
||||
Vector<Uint32> dynamicBindings;
|
||||
};
|
||||
|
||||
static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment);
|
||||
static Uint64 ComputeProgramHash(const MG_State::GLState::ProgramObject& program);
|
||||
static Bool IsSamplerUniformType(GLenum glType);
|
||||
Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const;
|
||||
ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program);
|
||||
Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset);
|
||||
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
|
||||
Vector<VkDeviceSize>& outSizes) const;
|
||||
void DestroyProgramLayouts();
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VmaAllocator m_allocator = nullptr;
|
||||
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
Vector<VkDescriptorSet> m_descriptorSets;
|
||||
Vector<FrameResources> m_frames;
|
||||
UnorderedMap<Uint64, ProgramLayout> m_programLayouts;
|
||||
|
||||
VkDeviceSize m_minDynamicOffsetAlignment = 1;
|
||||
VkDeviceSize m_perFrameUploadBytes = 0;
|
||||
Uint32 m_frameCount = 0;
|
||||
Uint32 m_maxBindings = 0;
|
||||
Uint32 m_setsPerFrame = 0;
|
||||
VkTextureSamplerManager* m_textureSamplerManager = nullptr;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#include "VkTextureSamplerManager.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool VkTextureSamplerManager::Initialize(const InitInfo& initInfo) {
|
||||
Shutdown();
|
||||
m_device = initInfo.device;
|
||||
m_physicalDevice = initInfo.physicalDevice;
|
||||
m_commandPool = initInfo.commandPool;
|
||||
m_graphicsQueue = initInfo.graphicsQueue;
|
||||
|
||||
if (m_device == VK_NULL_HANDLE || m_physicalDevice == VK_NULL_HANDLE || m_commandPool == VK_NULL_HANDLE ||
|
||||
m_graphicsQueue == VK_NULL_HANDLE) {
|
||||
MGLOG_E("VkTextureSamplerManager::Initialize failed: invalid init handles");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent.width = 1;
|
||||
imageInfo.extent.height = 1;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &m_fallbackImage),
|
||||
"VkTextureSamplerManager::Initialize, vkCreateImage");
|
||||
|
||||
VkMemoryRequirements memoryRequirements{};
|
||||
vkGetImageMemoryRequirements(m_device, m_fallbackImage, &memoryRequirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memoryRequirements.size;
|
||||
allocInfo.memoryTypeIndex = FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &m_fallbackImageMemory),
|
||||
"VkTextureSamplerManager::Initialize, vkAllocateMemory");
|
||||
VK_VERIFY(vkBindImageMemory(m_device, m_fallbackImage, m_fallbackImageMemory, 0),
|
||||
"VkTextureSamplerManager::Initialize, vkBindImageMemory");
|
||||
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = m_fallbackImage;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &m_fallbackImageView),
|
||||
"VkTextureSamplerManager::Initialize, vkCreateImageView");
|
||||
|
||||
VkSamplerCreateInfo samplerInfo{};
|
||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
samplerInfo.mipLodBias = 0.0f;
|
||||
samplerInfo.anisotropyEnable = VK_FALSE;
|
||||
samplerInfo.maxAnisotropy = 1.0f;
|
||||
samplerInfo.compareEnable = VK_FALSE;
|
||||
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
samplerInfo.minLod = 0.0f;
|
||||
samplerInfo.maxLod = 0.0f;
|
||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
samplerInfo.unnormalizedCoordinates = VK_FALSE;
|
||||
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &m_fallbackSampler),
|
||||
"VkTextureSamplerManager::Initialize, vkCreateSampler");
|
||||
|
||||
if (!UploadFallbackTexture()) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VkTextureSamplerManager::Shutdown() {
|
||||
if (m_device != VK_NULL_HANDLE && m_fallbackSampler != VK_NULL_HANDLE) {
|
||||
vkDestroySampler(m_device, m_fallbackSampler, nullptr);
|
||||
}
|
||||
m_fallbackSampler = VK_NULL_HANDLE;
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_fallbackImageView != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(m_device, m_fallbackImageView, nullptr);
|
||||
}
|
||||
m_fallbackImageView = VK_NULL_HANDLE;
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_fallbackImage != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(m_device, m_fallbackImage, nullptr);
|
||||
}
|
||||
m_fallbackImage = VK_NULL_HANDLE;
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_fallbackImageMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_fallbackImageMemory, nullptr);
|
||||
}
|
||||
m_fallbackImageMemory = VK_NULL_HANDLE;
|
||||
|
||||
m_device = VK_NULL_HANDLE;
|
||||
m_physicalDevice = VK_NULL_HANDLE;
|
||||
m_commandPool = VK_NULL_HANDLE;
|
||||
m_graphicsQueue = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const {
|
||||
if (m_fallbackSampler == VK_NULL_HANDLE || m_fallbackImageView == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
outImageInfo.sampler = m_fallbackSampler;
|
||||
outImageInfo.imageView = m_fallbackImageView;
|
||||
outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
Uint32 VkTextureSamplerManager::FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const {
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties{};
|
||||
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
||||
for (Uint32 i = 0; i < memoryProperties.memoryTypeCount; ++i) {
|
||||
if ((typeFilter & (1U << i)) &&
|
||||
(memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
MOBILEGL_ASSERT(false, "VkTextureSamplerManager::FindMemoryType failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::UploadFallbackTexture() {
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.commandPool = m_commandPool;
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkAllocateCommandBuffers");
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkBeginCommandBuffer");
|
||||
|
||||
VkImageMemoryBarrier toTransfer{};
|
||||
toTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toTransfer.srcAccessMask = 0;
|
||||
toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.image = m_fallbackImage;
|
||||
toTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
toTransfer.subresourceRange.baseMipLevel = 0;
|
||||
toTransfer.subresourceRange.levelCount = 1;
|
||||
toTransfer.subresourceRange.baseArrayLayer = 0;
|
||||
toTransfer.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &toTransfer);
|
||||
|
||||
VkClearColorValue clearColor{};
|
||||
clearColor.float32[0] = 1.0f;
|
||||
clearColor.float32[1] = 1.0f;
|
||||
clearColor.float32[2] = 1.0f;
|
||||
clearColor.float32[3] = 1.0f;
|
||||
VkImageSubresourceRange clearRange{};
|
||||
clearRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clearRange.baseMipLevel = 0;
|
||||
clearRange.levelCount = 1;
|
||||
clearRange.baseArrayLayer = 0;
|
||||
clearRange.layerCount = 1;
|
||||
vkCmdClearColorImage(commandBuffer, m_fallbackImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1,
|
||||
&clearRange);
|
||||
|
||||
VkImageMemoryBarrier toShaderRead{};
|
||||
toShaderRead.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toShaderRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toShaderRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toShaderRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toShaderRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
toShaderRead.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toShaderRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toShaderRead.image = m_fallbackImage;
|
||||
toShaderRead.subresourceRange = clearRange;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
||||
0, nullptr, 0, nullptr, 1, &toShaderRead);
|
||||
|
||||
VK_VERIFY(vkEndCommandBuffer(commandBuffer),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkEndCommandBuffer");
|
||||
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkQueueSubmit");
|
||||
VK_VERIFY(vkQueueWaitIdle(m_graphicsQueue),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkQueueWaitIdle");
|
||||
|
||||
vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class VkTextureSamplerManager {
|
||||
public:
|
||||
struct InitInfo {
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||
VkQueue graphicsQueue = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
Bool Initialize(const InitInfo& initInfo);
|
||||
void Shutdown();
|
||||
|
||||
Bool GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const;
|
||||
|
||||
private:
|
||||
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
|
||||
Bool UploadFallbackTexture();
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||
VkCommandPool m_commandPool = VK_NULL_HANDLE;
|
||||
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
|
||||
|
||||
VkImage m_fallbackImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory m_fallbackImageMemory = VK_NULL_HANDLE;
|
||||
VkImageView m_fallbackImageView = VK_NULL_HANDLE;
|
||||
VkSampler m_fallbackSampler = VK_NULL_HANDLE;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
VkPipeline VulkanRenderer::GetOrCreatePipeline(
|
||||
const MG_State::GLState::ProgramObject& program, Uint64 vertexInputHash,
|
||||
const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, Uint64 vertexInputHash,
|
||||
const VkPipelineVertexInputStateCreateInfo& vertexInputState) {
|
||||
MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized");
|
||||
MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized");
|
||||
@@ -76,7 +76,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
PipelineFactory::PipelineCreatePayload payload{};
|
||||
payload.programHash = programHash;
|
||||
payload.vertexInputHash = vertexInputHash;
|
||||
payload.pipelineLayout = m_pipelineLayout;
|
||||
payload.pipelineLayout = pipelineLayout;
|
||||
payload.renderPass = (m_activeRenderPass != VK_NULL_HANDLE) ? m_activeRenderPass : m_renderPassLoad;
|
||||
payload.subpass = 0;
|
||||
payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
@@ -89,14 +89,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MGLOG_D("PrepareDemoPipeline called");
|
||||
|
||||
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
VkDescriptorSetLayout descriptorSetLayouts[1] = {VK_NULL_HANDLE};
|
||||
if (m_uniformDescriptorBinder) {
|
||||
descriptorSetLayouts[0] = m_uniformDescriptorBinder->GetDescriptorSetLayout();
|
||||
}
|
||||
if (descriptorSetLayouts[0] != VK_NULL_HANDLE) {
|
||||
plci.setLayoutCount = 1;
|
||||
plci.pSetLayouts = descriptorSetLayouts;
|
||||
}
|
||||
VK_VERIFY(vkCreatePipelineLayout(m_device, &plci, nullptr, &m_pipelineLayout), "vkCreatePipelineLayout");
|
||||
|
||||
MGLOG_I("PrepareDemoPipeline completed");
|
||||
@@ -124,10 +116,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
m_pipelineFactory = MakeUnique<PipelineFactory>(m_device, m_config);
|
||||
m_programFactory = MakeUnique<ProgramFactory>(m_device, m_config);
|
||||
m_textureSamplerManager = MakeUnique<VkTextureSamplerManager>();
|
||||
if (!m_textureSamplerManager->Initialize({m_device, m_physicalDevice.handle, m_commandPool, m_graphicsQueue})) {
|
||||
MGLOG_E("VkTextureSamplerManager initialization failed. Sampler/texture descriptors will fallback.");
|
||||
m_textureSamplerManager.reset();
|
||||
}
|
||||
m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>();
|
||||
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
|
||||
m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
|
||||
m_config.MaxFramesInFlight)) {
|
||||
m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024,
|
||||
m_textureSamplerManager.get())) {
|
||||
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
|
||||
m_uniformDescriptorBinder.reset();
|
||||
}
|
||||
@@ -155,6 +153,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
m_pipelineFactory.reset();
|
||||
m_programFactory.reset();
|
||||
if (m_textureSamplerManager) {
|
||||
m_textureSamplerManager->Shutdown();
|
||||
m_textureSamplerManager.reset();
|
||||
}
|
||||
m_vertexInputStateFactory.reset();
|
||||
if (m_framebufferManager) {
|
||||
m_framebufferManager->Shutdown();
|
||||
@@ -677,7 +679,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
vertexInputInfo = &emptyVertexInputBuilder.Build();
|
||||
}
|
||||
|
||||
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.program, vertexInputHash, *vertexInputInfo);
|
||||
VkPipelineLayout pipelineLayoutToUse = m_pipelineLayout;
|
||||
if (m_uniformDescriptorBinder) {
|
||||
pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.program);
|
||||
if (pipelineLayoutToUse == VK_NULL_HANDLE) {
|
||||
MGLOG_W("DrawArrays skipped: failed to get pipeline layout for program");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.program, pipelineLayoutToUse, vertexInputHash,
|
||||
*vertexInputInfo);
|
||||
if (pipelineToBind == VK_NULL_HANDLE) {
|
||||
MGLOG_W("DrawArrays skipped: failed to create/get pipeline");
|
||||
return;
|
||||
@@ -685,7 +697,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||
if (m_uniformDescriptorBinder &&
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout, *payload.program,
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, *payload.program,
|
||||
m_frameContext.GetCurrentFrameIndex())) {
|
||||
MGLOG_W("DrawArrays skipped: failed to bind uniform descriptors");
|
||||
return;
|
||||
@@ -765,7 +777,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
vertexInputInfo = &emptyVertexInputBuilder.Build();
|
||||
}
|
||||
|
||||
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.drawArray.program, vertexInputHash, *vertexInputInfo);
|
||||
VkPipelineLayout pipelineLayoutToUse = m_pipelineLayout;
|
||||
if (m_uniformDescriptorBinder) {
|
||||
pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.drawArray.program);
|
||||
if (pipelineLayoutToUse == VK_NULL_HANDLE) {
|
||||
MGLOG_W("DrawElements skipped: failed to get pipeline layout for program");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.drawArray.program, pipelineLayoutToUse, vertexInputHash,
|
||||
*vertexInputInfo);
|
||||
if (pipelineToBind == VK_NULL_HANDLE) {
|
||||
MGLOG_W("DrawElements skipped: failed to create/get pipeline");
|
||||
return;
|
||||
@@ -794,7 +816,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||
if (m_uniformDescriptorBinder &&
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout,
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse,
|
||||
*payload.drawArray.program,
|
||||
m_frameContext.GetCurrentFrameIndex())) {
|
||||
MGLOG_W("DrawElements skipped: failed to bind uniform descriptors");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "VertexInputStateFactory.h"
|
||||
#include "VkBufferObject.h"
|
||||
#include "VkFramebufferManager.h"
|
||||
#include "VkTextureSamplerManager.h"
|
||||
#include "MG_Util/Math/VectorTypes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
@@ -152,6 +153,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder;
|
||||
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
|
||||
UniquePtr<VkFramebufferManager> m_framebufferManager;
|
||||
UniquePtr<VkTextureSamplerManager> m_textureSamplerManager;
|
||||
|
||||
void CreateInstance();
|
||||
VkResult SetupDebugMessenger();
|
||||
@@ -171,7 +173,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkRenderPass CreateDefaultRenderPass(VkAttachmentLoadOp loadOp);
|
||||
void CreateDefaultFramebuffers();
|
||||
void PrepareDemoPipeline();
|
||||
VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, Uint64 vertexInputHash,
|
||||
VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout,
|
||||
Uint64 vertexInputHash,
|
||||
const VkPipelineVertexInputStateCreateInfo& vertexInputState);
|
||||
void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
||||
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
||||
|
||||
Reference in New Issue
Block a user