mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +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/VertexInputStateFactory.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.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/Core.cpp
|
||||||
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
||||||
|
|||||||
@@ -20,9 +20,72 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return (value + alignment - 1) / alignment * alignment;
|
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,
|
Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator,
|
||||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||||
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes) {
|
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes,
|
||||||
|
VkTextureSamplerManager* textureSamplerManager) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
||||||
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
|
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
|
||||||
@@ -38,53 +101,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_frameCount = frameCount;
|
m_frameCount = frameCount;
|
||||||
m_maxBindings = maxBindings;
|
m_maxBindings = maxBindings;
|
||||||
m_setsPerFrame = setsPerFrame;
|
m_setsPerFrame = setsPerFrame;
|
||||||
|
m_textureSamplerManager = textureSamplerManager;
|
||||||
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_frames.resize(m_frameCount);
|
m_frames.resize(m_frameCount);
|
||||||
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
|
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
|
||||||
auto& frame = m_frames[frameIndex];
|
auto& frame = m_frames[frameIndex];
|
||||||
frame.writeCursor = 0;
|
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(
|
const Bool created = frame.uploadBuffer.Create(
|
||||||
m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO,
|
m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO,
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
|
||||||
@@ -101,21 +139,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void UniformDescriptorBinder::Shutdown() {
|
void UniformDescriptorBinder::Shutdown() {
|
||||||
for (auto& frame : m_frames) {
|
for (auto& frame : m_frames) {
|
||||||
frame.uploadBuffer.Destroy();
|
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.writeCursor = 0;
|
||||||
frame.descriptorCursor = 0;
|
|
||||||
}
|
}
|
||||||
m_frames.clear();
|
m_frames.clear();
|
||||||
m_descriptorSets.clear();
|
DestroyProgramLayouts();
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
m_allocator = nullptr;
|
m_allocator = nullptr;
|
||||||
m_device = VK_NULL_HANDLE;
|
m_device = VK_NULL_HANDLE;
|
||||||
@@ -124,13 +155,141 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_frameCount = 0;
|
m_frameCount = 0;
|
||||||
m_maxBindings = 0;
|
m_maxBindings = 0;
|
||||||
m_setsPerFrame = 0;
|
m_setsPerFrame = 0;
|
||||||
|
m_textureSamplerManager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
|
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
|
||||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
||||||
auto& frame = m_frames[frameIndex];
|
auto& frame = m_frames[frameIndex];
|
||||||
frame.writeCursor = 0;
|
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) {
|
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,
|
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||||
const MG_State::GLState::ProgramObject& program,
|
const MG_State::GLState::ProgramObject& program,
|
||||||
Uint32 frameIndex) {
|
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");
|
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -230,16 +389,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& frame = m_frames[frameIndex];
|
ProgramLayout* layout = GetOrCreateProgramLayout(program);
|
||||||
if (frame.descriptorCursor >= m_setsPerFrame) {
|
if (!layout || layout->pipelineLayout == VK_NULL_HANDLE || layout->descriptorSetLayout == VK_NULL_HANDLE) {
|
||||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: per-frame descriptor set budget exceeded");
|
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot get program layout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (layout->pipelineLayout != pipelineLayout) {
|
||||||
|
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: pipelineLayout mismatch");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Uint32 descriptorSetIndex = frameIndex * m_setsPerFrame + frame.descriptorCursor;
|
auto& frame = m_frames[frameIndex];
|
||||||
const VkDescriptorSet descriptorSet = m_descriptorSets[descriptorSetIndex];
|
if (frame.descriptorPool == VK_NULL_HANDLE) {
|
||||||
++frame.descriptorCursor;
|
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<const void*> bindingData;
|
||||||
Vector<VkDeviceSize> bindingSizes;
|
Vector<VkDeviceSize> bindingSizes;
|
||||||
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
|
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
|
||||||
@@ -248,51 +421,92 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const Uint8 kFallbackData[16] = {};
|
static const Uint8 kFallbackData[16] = {};
|
||||||
|
VkDescriptorImageInfo fallbackImageInfo{};
|
||||||
|
const Bool hasFallbackImage = m_textureSamplerManager && m_textureSamplerManager->GetFallbackDescriptor(fallbackImageInfo);
|
||||||
|
|
||||||
Vector<VkDescriptorBufferInfo> bufferInfos(m_maxBindings);
|
|
||||||
Vector<VkWriteDescriptorSet> writes;
|
Vector<VkWriteDescriptorSet> writes;
|
||||||
writes.reserve(m_maxBindings);
|
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) {
|
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
|
||||||
const void* payload = bindingData[binding];
|
const auto kind = layout->bindingKinds[binding];
|
||||||
VkDeviceSize payloadSize = bindingSizes[binding];
|
if (kind == BindingKind::None) {
|
||||||
if (payload == nullptr || payloadSize == 0) {
|
continue;
|
||||||
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: 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{};
|
VkWriteDescriptorSet write{};
|
||||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
write.dstSet = descriptorSet;
|
write.dstSet = descriptorSet;
|
||||||
write.dstBinding = binding;
|
write.dstBinding = binding;
|
||||||
write.dstArrayElement = 0;
|
write.dstArrayElement = 0;
|
||||||
write.descriptorCount = 1;
|
write.descriptorCount = 1;
|
||||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
||||||
write.pBufferInfo = &bufferInfos[binding];
|
if (kind == BindingKind::UniformBufferDynamic) {
|
||||||
writes.push_back(write);
|
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,
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet,
|
||||||
static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
|
static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
|
||||||
return true;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "VkBufferObject.h"
|
#include "VkBufferObject.h"
|
||||||
|
#include "VkTextureSamplerManager.h"
|
||||||
#include "../VkIncludes.h"
|
#include "../VkIncludes.h"
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
@@ -20,42 +21,59 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class UniformDescriptorBinder {
|
class UniformDescriptorBinder {
|
||||||
public:
|
public:
|
||||||
|
enum class BindingKind : Uint8 {
|
||||||
|
None = 0,
|
||||||
|
UniformBufferDynamic,
|
||||||
|
CombinedImageSampler
|
||||||
|
};
|
||||||
|
|
||||||
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
|
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
|
||||||
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
|
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
|
||||||
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024);
|
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024,
|
||||||
|
VkTextureSamplerManager* textureSamplerManager = nullptr);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
void BeginFrame(Uint32 frameIndex);
|
void BeginFrame(Uint32 frameIndex);
|
||||||
|
VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program);
|
||||||
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||||
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
|
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
|
||||||
|
|
||||||
VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct FrameResources {
|
struct FrameResources {
|
||||||
VkBufferObject uploadBuffer;
|
VkBufferObject uploadBuffer;
|
||||||
|
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||||
VkDeviceSize writeCursor = 0;
|
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 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 AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset);
|
||||||
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
|
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
|
||||||
Vector<VkDeviceSize>& outSizes) const;
|
Vector<VkDeviceSize>& outSizes) const;
|
||||||
|
void DestroyProgramLayouts();
|
||||||
|
|
||||||
VkDevice m_device = VK_NULL_HANDLE;
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
VmaAllocator m_allocator = nullptr;
|
VmaAllocator m_allocator = nullptr;
|
||||||
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
|
|
||||||
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
Vector<VkDescriptorSet> m_descriptorSets;
|
|
||||||
Vector<FrameResources> m_frames;
|
Vector<FrameResources> m_frames;
|
||||||
|
UnorderedMap<Uint64, ProgramLayout> m_programLayouts;
|
||||||
|
|
||||||
VkDeviceSize m_minDynamicOffsetAlignment = 1;
|
VkDeviceSize m_minDynamicOffsetAlignment = 1;
|
||||||
VkDeviceSize m_perFrameUploadBytes = 0;
|
VkDeviceSize m_perFrameUploadBytes = 0;
|
||||||
Uint32 m_frameCount = 0;
|
Uint32 m_frameCount = 0;
|
||||||
Uint32 m_maxBindings = 0;
|
Uint32 m_maxBindings = 0;
|
||||||
Uint32 m_setsPerFrame = 0;
|
Uint32 m_setsPerFrame = 0;
|
||||||
|
VkTextureSamplerManager* m_textureSamplerManager = nullptr;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // 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(
|
VkPipeline VulkanRenderer::GetOrCreatePipeline(
|
||||||
const MG_State::GLState::ProgramObject& program, Uint64 vertexInputHash,
|
const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, Uint64 vertexInputHash,
|
||||||
const VkPipelineVertexInputStateCreateInfo& vertexInputState) {
|
const VkPipelineVertexInputStateCreateInfo& vertexInputState) {
|
||||||
MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized");
|
MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized");
|
||||||
MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized");
|
MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized");
|
||||||
@@ -76,7 +76,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
PipelineFactory::PipelineCreatePayload payload{};
|
PipelineFactory::PipelineCreatePayload payload{};
|
||||||
payload.programHash = programHash;
|
payload.programHash = programHash;
|
||||||
payload.vertexInputHash = vertexInputHash;
|
payload.vertexInputHash = vertexInputHash;
|
||||||
payload.pipelineLayout = m_pipelineLayout;
|
payload.pipelineLayout = pipelineLayout;
|
||||||
payload.renderPass = (m_activeRenderPass != VK_NULL_HANDLE) ? m_activeRenderPass : m_renderPassLoad;
|
payload.renderPass = (m_activeRenderPass != VK_NULL_HANDLE) ? m_activeRenderPass : m_renderPassLoad;
|
||||||
payload.subpass = 0;
|
payload.subpass = 0;
|
||||||
payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||||
@@ -89,14 +89,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MGLOG_D("PrepareDemoPipeline called");
|
MGLOG_D("PrepareDemoPipeline called");
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
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");
|
VK_VERIFY(vkCreatePipelineLayout(m_device, &plci, nullptr, &m_pipelineLayout), "vkCreatePipelineLayout");
|
||||||
|
|
||||||
MGLOG_I("PrepareDemoPipeline completed");
|
MGLOG_I("PrepareDemoPipeline completed");
|
||||||
@@ -124,10 +116,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
m_pipelineFactory = MakeUnique<PipelineFactory>(m_device, m_config);
|
m_pipelineFactory = MakeUnique<PipelineFactory>(m_device, m_config);
|
||||||
m_programFactory = MakeUnique<ProgramFactory>(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>();
|
m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>();
|
||||||
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
|
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
|
||||||
m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
|
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.");
|
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
|
||||||
m_uniformDescriptorBinder.reset();
|
m_uniformDescriptorBinder.reset();
|
||||||
}
|
}
|
||||||
@@ -155,6 +153,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
m_pipelineFactory.reset();
|
m_pipelineFactory.reset();
|
||||||
m_programFactory.reset();
|
m_programFactory.reset();
|
||||||
|
if (m_textureSamplerManager) {
|
||||||
|
m_textureSamplerManager->Shutdown();
|
||||||
|
m_textureSamplerManager.reset();
|
||||||
|
}
|
||||||
m_vertexInputStateFactory.reset();
|
m_vertexInputStateFactory.reset();
|
||||||
if (m_framebufferManager) {
|
if (m_framebufferManager) {
|
||||||
m_framebufferManager->Shutdown();
|
m_framebufferManager->Shutdown();
|
||||||
@@ -677,7 +679,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
vertexInputInfo = &emptyVertexInputBuilder.Build();
|
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) {
|
if (pipelineToBind == VK_NULL_HANDLE) {
|
||||||
MGLOG_W("DrawArrays skipped: failed to create/get pipeline");
|
MGLOG_W("DrawArrays skipped: failed to create/get pipeline");
|
||||||
return;
|
return;
|
||||||
@@ -685,7 +697,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||||
if (m_uniformDescriptorBinder &&
|
if (m_uniformDescriptorBinder &&
|
||||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout, *payload.program,
|
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, *payload.program,
|
||||||
m_frameContext.GetCurrentFrameIndex())) {
|
m_frameContext.GetCurrentFrameIndex())) {
|
||||||
MGLOG_W("DrawArrays skipped: failed to bind uniform descriptors");
|
MGLOG_W("DrawArrays skipped: failed to bind uniform descriptors");
|
||||||
return;
|
return;
|
||||||
@@ -765,7 +777,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
vertexInputInfo = &emptyVertexInputBuilder.Build();
|
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) {
|
if (pipelineToBind == VK_NULL_HANDLE) {
|
||||||
MGLOG_W("DrawElements skipped: failed to create/get pipeline");
|
MGLOG_W("DrawElements skipped: failed to create/get pipeline");
|
||||||
return;
|
return;
|
||||||
@@ -794,7 +816,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||||
if (m_uniformDescriptorBinder &&
|
if (m_uniformDescriptorBinder &&
|
||||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout,
|
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse,
|
||||||
*payload.drawArray.program,
|
*payload.drawArray.program,
|
||||||
m_frameContext.GetCurrentFrameIndex())) {
|
m_frameContext.GetCurrentFrameIndex())) {
|
||||||
MGLOG_W("DrawElements skipped: failed to bind uniform descriptors");
|
MGLOG_W("DrawElements skipped: failed to bind uniform descriptors");
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "VertexInputStateFactory.h"
|
#include "VertexInputStateFactory.h"
|
||||||
#include "VkBufferObject.h"
|
#include "VkBufferObject.h"
|
||||||
#include "VkFramebufferManager.h"
|
#include "VkFramebufferManager.h"
|
||||||
|
#include "VkTextureSamplerManager.h"
|
||||||
#include "MG_Util/Math/VectorTypes.h"
|
#include "MG_Util/Math/VectorTypes.h"
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
@@ -152,6 +153,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder;
|
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder;
|
||||||
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
|
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
|
||||||
UniquePtr<VkFramebufferManager> m_framebufferManager;
|
UniquePtr<VkFramebufferManager> m_framebufferManager;
|
||||||
|
UniquePtr<VkTextureSamplerManager> m_textureSamplerManager;
|
||||||
|
|
||||||
void CreateInstance();
|
void CreateInstance();
|
||||||
VkResult SetupDebugMessenger();
|
VkResult SetupDebugMessenger();
|
||||||
@@ -171,7 +173,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VkRenderPass CreateDefaultRenderPass(VkAttachmentLoadOp loadOp);
|
VkRenderPass CreateDefaultRenderPass(VkAttachmentLoadOp loadOp);
|
||||||
void CreateDefaultFramebuffers();
|
void CreateDefaultFramebuffers();
|
||||||
void PrepareDemoPipeline();
|
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);
|
const VkPipelineVertexInputStateCreateInfo& vertexInputState);
|
||||||
void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
||||||
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
|
||||||
|
|||||||
Reference in New Issue
Block a user