mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18:30 +09:00
[Feat] (MG_Backend/DirectVulkan): initial real implementation of VkTextureSamplerManager
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "UniformDescriptorBinder.h"
|
||||
|
||||
#include "VkFramebufferManager.h"
|
||||
#include "MG_State/GLState/Core.h"
|
||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||
@@ -82,10 +83,62 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
}
|
||||
|
||||
TextureTarget UniformDescriptorBinder::UniformTypeToTextureTarget(GLenum glType) {
|
||||
switch (glType) {
|
||||
case GL_SAMPLER_1D:
|
||||
case GL_INT_SAMPLER_1D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D:
|
||||
return TextureTarget::Texture1D;
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
return TextureTarget::Texture3D;
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
return TextureTarget::TextureCubeMap;
|
||||
case GL_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
return TextureTarget::Texture2DMultisample;
|
||||
case GL_SAMPLER_BUFFER:
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
||||
return TextureTarget::TextureBuffer;
|
||||
case GL_SAMPLER_1D_ARRAY:
|
||||
case GL_SAMPLER_1D_ARRAY_SHADOW:
|
||||
case GL_INT_SAMPLER_1D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
|
||||
return TextureTarget::Texture1DArray;
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
return TextureTarget::Texture2DArray;
|
||||
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
return TextureTarget::Texture2DMultisampleArray;
|
||||
case GL_SAMPLER_2D_RECT:
|
||||
case GL_SAMPLER_2D_RECT_SHADOW:
|
||||
case GL_INT_SAMPLER_2D_RECT:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
|
||||
return TextureTarget::TextureRectangle;
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
default:
|
||||
return TextureTarget::Texture2D;
|
||||
}
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator,
|
||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes,
|
||||
VkTextureSamplerManager* textureSamplerManager) {
|
||||
VkTextureSamplerManager* textureSamplerManager,
|
||||
VkFramebufferManager* framebufferManager) {
|
||||
Shutdown();
|
||||
|
||||
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
|
||||
@@ -102,6 +155,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_maxBindings = maxBindings;
|
||||
m_setsPerFrame = setsPerFrame;
|
||||
m_textureSamplerManager = textureSamplerManager;
|
||||
m_framebufferManager = framebufferManager;
|
||||
|
||||
m_frames.resize(m_frameCount);
|
||||
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
|
||||
@@ -156,6 +210,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_maxBindings = 0;
|
||||
m_setsPerFrame = 0;
|
||||
m_textureSamplerManager = nullptr;
|
||||
m_framebufferManager = nullptr;
|
||||
}
|
||||
|
||||
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
|
||||
@@ -232,6 +287,130 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program,
|
||||
ProgramLayout& layout) const {
|
||||
layout.samplerUniformLocationByBinding.assign(m_maxBindings, -1);
|
||||
layout.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D);
|
||||
|
||||
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;
|
||||
}
|
||||
if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
|
||||
&compiler) != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) {
|
||||
spvc_context_destroy(context);
|
||||
continue;
|
||||
}
|
||||
|
||||
const spvc_reflected_resource* list = nullptr;
|
||||
size_t count = 0;
|
||||
if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, &list, &count) ==
|
||||
SPVC_SUCCESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
String uniformName = list[i].name ? list[i].name : "";
|
||||
Int location = program.GetUniformLocation(uniformName);
|
||||
if (location < 0) {
|
||||
const auto arraySuffix = uniformName.find("[0]");
|
||||
if (arraySuffix != String::npos) {
|
||||
uniformName = uniformName.substr(0, arraySuffix);
|
||||
location = program.GetUniformLocation(uniformName);
|
||||
}
|
||||
}
|
||||
if (location < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
layout.samplerUniformLocationByBinding[binding] = location;
|
||||
layout.samplerTextureTargetByBinding[binding] =
|
||||
UniformTypeToTextureTarget(program.GetUniformType(static_cast<Uint>(location)));
|
||||
}
|
||||
}
|
||||
|
||||
spvc_context_destroy(context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
const ProgramLayout& layout, Uint32 binding,
|
||||
VkDescriptorImageInfo& outImageInfo) const {
|
||||
if (!m_textureSamplerManager || !MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Int location = layout.samplerUniformLocationByBinding[binding];
|
||||
if (location < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
|
||||
if (unit < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
|
||||
const auto samplerOverride = textureUnit.GetSamplerObject();
|
||||
|
||||
const TextureTarget preferredTarget = layout.samplerTextureTargetByBinding[binding];
|
||||
auto texture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject();
|
||||
if (!texture) {
|
||||
auto& slots = textureUnit.GetAllBindingSlots();
|
||||
for (auto& slot : slots) {
|
||||
texture = slot.GetBoundObject();
|
||||
if (texture) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!texture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_framebufferManager &&
|
||||
m_framebufferManager->TransitionOffscreenColorTextureToShaderRead(commandBuffer, texture->GetExternalIndex())) {
|
||||
VkImageView offscreenView = VK_NULL_HANDLE;
|
||||
if (m_framebufferManager->GetOffscreenColorViewByTexture(texture->GetExternalIndex(), offscreenView) &&
|
||||
offscreenView != VK_NULL_HANDLE) {
|
||||
VkDescriptorImageInfo sampledInfo{};
|
||||
if (!m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), sampledInfo)) {
|
||||
return false;
|
||||
}
|
||||
sampledInfo.imageView = offscreenView;
|
||||
sampledInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
outImageInfo = sampledInfo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), outImageInfo);
|
||||
}
|
||||
|
||||
UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout(
|
||||
const MG_State::GLState::ProgramObject& program) {
|
||||
const Uint64 hash = ComputeProgramHash(program);
|
||||
@@ -246,6 +425,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: reflection failed");
|
||||
return nullptr;
|
||||
}
|
||||
if (!ReflectSamplerBindings(program, layout)) {
|
||||
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: sampler reflection failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(m_maxBindings);
|
||||
@@ -476,11 +659,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
writes.push_back(write);
|
||||
dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset));
|
||||
} else {
|
||||
if (!hasFallbackImage) {
|
||||
VkDescriptorImageInfo imageInfo{};
|
||||
Bool hasImage = ResolveSamplerDescriptor(commandBuffer, program, *layout, binding, imageInfo);
|
||||
if (!hasImage) {
|
||||
if (!hasFallbackImage) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable");
|
||||
return false;
|
||||
}
|
||||
imageInfo = fallbackImageInfo;
|
||||
}
|
||||
if (imageInfo.sampler == VK_NULL_HANDLE || imageInfo.imageView == VK_NULL_HANDLE) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable");
|
||||
return false;
|
||||
}
|
||||
imageInfos.push_back(fallbackImageInfo);
|
||||
imageInfos.push_back(imageInfo);
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imageInfos.back();
|
||||
writes.push_back(write);
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class VkFramebufferManager;
|
||||
|
||||
class UniformDescriptorBinder {
|
||||
public:
|
||||
enum class BindingKind : Uint8 {
|
||||
@@ -30,7 +32,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
|
||||
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
|
||||
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024,
|
||||
VkTextureSamplerManager* textureSamplerManager = nullptr);
|
||||
VkTextureSamplerManager* textureSamplerManager = nullptr,
|
||||
VkFramebufferManager* framebufferManager = nullptr);
|
||||
void Shutdown();
|
||||
|
||||
void BeginFrame(Uint32 frameIndex);
|
||||
@@ -51,11 +54,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
Vector<BindingKind> bindingKinds;
|
||||
Vector<Uint32> dynamicBindings;
|
||||
Vector<Int> samplerUniformLocationByBinding;
|
||||
Vector<TextureTarget> samplerTextureTargetByBinding;
|
||||
};
|
||||
|
||||
static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment);
|
||||
static Uint64 ComputeProgramHash(const MG_State::GLState::ProgramObject& program);
|
||||
static Bool IsSamplerUniformType(GLenum glType);
|
||||
static TextureTarget UniformTypeToTextureTarget(GLenum glType);
|
||||
Bool ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, ProgramLayout& layout) const;
|
||||
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program,
|
||||
const ProgramLayout& layout, Uint32 binding,
|
||||
VkDescriptorImageInfo& outImageInfo) const;
|
||||
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);
|
||||
@@ -74,6 +84,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 m_maxBindings = 0;
|
||||
Uint32 m_setsPerFrame = 0;
|
||||
VkTextureSamplerManager* m_textureSamplerManager = nullptr;
|
||||
VkFramebufferManager* m_framebufferManager = nullptr;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
|
||||
@@ -107,6 +107,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
Bool VkFramebufferManager::TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer,
|
||||
Uint textureExternalIndex) {
|
||||
for (auto& [_, target] : m_offscreenColorTargets) {
|
||||
if (target.colorTextureExternalIndex != textureExternalIndex || target.image == VK_NULL_HANDLE) {
|
||||
continue;
|
||||
}
|
||||
const Bool fromUndefined = (target.layout == VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
return TransitionImageLayout(commandBuffer,
|
||||
target.image,
|
||||
target.layout,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
fromUndefined ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
|
||||
: (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT),
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
fromUndefined ? 0
|
||||
: (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT),
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool VkFramebufferManager::GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage,
|
||||
VkExtent2D& outExtent) const {
|
||||
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
|
||||
@@ -118,6 +142,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkFramebufferManager::GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const {
|
||||
for (const auto& [_, target] : m_offscreenColorTargets) {
|
||||
if (target.colorTextureExternalIndex != textureExternalIndex || target.imageView == VK_NULL_HANDLE) {
|
||||
continue;
|
||||
}
|
||||
outImageView = target.imageView;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool VkFramebufferManager::GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass,
|
||||
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
|
||||
VkFormat& outDepthStencilFormat) const {
|
||||
@@ -162,7 +197,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
imageInfo.format = format;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_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, &target.image), "vkCreateImage(offscreen color)");
|
||||
@@ -329,6 +365,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
target.depthStencilFormat = depthStencilFormat;
|
||||
target.glObjectVersion = glObjectVersion;
|
||||
target.colorTextureExternalIndex =
|
||||
(colorAttachment.IsTexture() && colorAttachment.GetTexture()) ? colorAttachment.GetTexture()->GetExternalIndex()
|
||||
: 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -371,6 +410,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
target.format = VK_FORMAT_UNDEFINED;
|
||||
target.depthStencilFormat = VK_FORMAT_UNDEFINED;
|
||||
target.glObjectVersion = 0;
|
||||
target.colorTextureExternalIndex = 0;
|
||||
}
|
||||
|
||||
Bool VkFramebufferManager::TransitionImageLayout(VkCommandBuffer commandBuffer,
|
||||
|
||||
@@ -29,7 +29,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool EnsureOffscreenColorTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo);
|
||||
Bool TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
|
||||
Bool TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
|
||||
Bool TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer, Uint textureExternalIndex);
|
||||
Bool GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent) const;
|
||||
Bool GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const;
|
||||
Bool GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer,
|
||||
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const;
|
||||
|
||||
@@ -49,6 +51,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkRenderPass renderPassLoad = VK_NULL_HANDLE;
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
Uint16 glObjectVersion = 0;
|
||||
Uint colorTextureExternalIndex = 0;
|
||||
};
|
||||
|
||||
Bool RecreateOffscreenColorTarget(OffscreenColorTarget& target,
|
||||
|
||||
@@ -8,9 +8,18 @@
|
||||
|
||||
#include "VkTextureSamplerManager.h"
|
||||
|
||||
#include "MG_State/GLState/Core.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
namespace {
|
||||
constexpr Uint64 BuildSamplerKey(Uint externalIndex, Uint16 version) {
|
||||
return (static_cast<Uint64>(externalIndex) << 16) | static_cast<Uint64>(version);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bool VkTextureSamplerManager::Initialize(const InitInfo& initInfo) {
|
||||
Shutdown();
|
||||
|
||||
m_device = initInfo.device;
|
||||
m_physicalDevice = initInfo.physicalDevice;
|
||||
m_commandPool = initInfo.commandPool;
|
||||
@@ -18,74 +27,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
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");
|
||||
MGLOG_E("VkTextureSamplerManager::Initialize failed: invalid Vulkan 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()) {
|
||||
MGLOG_E("VkTextureSamplerManager::Initialize failed: fallback texture creation failed");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
@@ -93,24 +41,34 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkTextureSamplerManager::Shutdown() {
|
||||
for (auto& [_, resource] : m_textureResources) {
|
||||
DestroyTextureResource(resource);
|
||||
}
|
||||
m_textureResources.clear();
|
||||
|
||||
for (auto& [_, sampler] : m_samplers) {
|
||||
if (m_device != VK_NULL_HANDLE && sampler.handle != VK_NULL_HANDLE) {
|
||||
vkDestroySampler(m_device, sampler.handle, nullptr);
|
||||
}
|
||||
sampler.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
m_samplers.clear();
|
||||
|
||||
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_fallbackSampler = VK_NULL_HANDLE;
|
||||
m_fallbackImageView = VK_NULL_HANDLE;
|
||||
m_fallbackImage = VK_NULL_HANDLE;
|
||||
m_fallbackImageMemory = VK_NULL_HANDLE;
|
||||
|
||||
m_device = VK_NULL_HANDLE;
|
||||
@@ -129,20 +87,247 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
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;
|
||||
Bool VkTextureSamplerManager::SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture,
|
||||
const MG_State::GLState::SamplerObject* samplerOverride,
|
||||
VkDescriptorImageInfo& outImageInfo) {
|
||||
if (m_device == VK_NULL_HANDLE) {
|
||||
return GetFallbackDescriptor(outImageInfo);
|
||||
}
|
||||
|
||||
auto it = m_textureResources.find(texture.GetExternalIndex());
|
||||
if (it == m_textureResources.end()) {
|
||||
TextureResource initial{};
|
||||
initial.textureExternalIndex = texture.GetExternalIndex();
|
||||
auto [insertIt, _] = m_textureResources.emplace(texture.GetExternalIndex(), initial);
|
||||
it = insertIt;
|
||||
}
|
||||
|
||||
if (!EnsureTextureSynced(it->second, texture)) {
|
||||
return GetFallbackDescriptor(outImageInfo);
|
||||
}
|
||||
|
||||
const MG_State::GLState::SamplerObject* samplerToUse = samplerOverride;
|
||||
if (!samplerToUse) {
|
||||
auto textureSampler = texture.GetSamplerObject();
|
||||
if (textureSampler) {
|
||||
samplerToUse = textureSampler.get();
|
||||
}
|
||||
}
|
||||
MOBILEGL_ASSERT(false, "VkTextureSamplerManager::FindMemoryType failed");
|
||||
return 0;
|
||||
|
||||
VkSampler sampler = m_fallbackSampler;
|
||||
if (samplerToUse) {
|
||||
sampler = GetOrCreateSampler(*samplerToUse);
|
||||
}
|
||||
if (sampler == VK_NULL_HANDLE) {
|
||||
sampler = m_fallbackSampler;
|
||||
}
|
||||
|
||||
if (it->second.view == VK_NULL_HANDLE || sampler == VK_NULL_HANDLE) {
|
||||
return GetFallbackDescriptor(outImageInfo);
|
||||
}
|
||||
|
||||
outImageInfo.sampler = sampler;
|
||||
outImageInfo.imageView = it->second.view;
|
||||
outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::UploadFallbackTexture() {
|
||||
Bool VkTextureSamplerManager::EnsureTextureSynced(TextureResource& resource,
|
||||
const MG_State::GLState::ITextureObject& texture) {
|
||||
TextureUploadTarget level0Target = TextureUploadTarget::Unknown;
|
||||
IntVec3 texelSize{0, 0, 0};
|
||||
SizeT byteSize = 0;
|
||||
if (!ResolveLevel0(texture, level0Target, texelSize, byteSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnsureTextureResource(resource, texture, level0Target, texelSize, byteSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* mipTexture = dynamic_cast<const MG_State::GLState::TextureObjectMipmap*>(&texture);
|
||||
if (!mipTexture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mipTexture->IsStorageDirty(level0Target, 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!UploadLevel0(resource, *mipTexture, level0Target, byteSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& mutableTexture = const_cast<MG_State::GLState::TextureObjectMipmap&>(*mipTexture);
|
||||
mutableTexture.MarkStorageDirty(level0Target, 0, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::EnsureTextureResource(TextureResource& resource,
|
||||
const MG_State::GLState::ITextureObject& texture,
|
||||
TextureUploadTarget level0Target, const IntVec3& texelSize,
|
||||
SizeT byteSize) {
|
||||
const VkFormat format = ResolveTextureFormat(texture.GetFormat());
|
||||
if (format == VK_FORMAT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
if (texelSize.x() <= 0 || texelSize.y() <= 0 || byteSize == 0) {
|
||||
return false;
|
||||
}
|
||||
if (level0Target != TextureUploadTarget::Texture2D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Bool compatible = resource.image != VK_NULL_HANDLE && resource.format == format &&
|
||||
resource.extent.width == static_cast<Uint32>(texelSize.x()) &&
|
||||
resource.extent.height == static_cast<Uint32>(texelSize.y());
|
||||
if (compatible) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DestroyTextureResource(resource);
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent.width = static_cast<Uint32>(texelSize.x());
|
||||
imageInfo.extent.height = static_cast<Uint32>(texelSize.y());
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.format = format;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &resource.image), "vkCreateImage(texture)");
|
||||
|
||||
VkMemoryRequirements requirements{};
|
||||
vkGetImageMemoryRequirements(m_device, resource.image, &requirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = requirements.size;
|
||||
allocInfo.memoryTypeIndex = FindMemoryType(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &resource.memory), "vkAllocateMemory(texture)");
|
||||
VK_VERIFY(vkBindImageMemory(m_device, resource.image, resource.memory, 0), "vkBindImageMemory(texture)");
|
||||
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = resource.image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = format;
|
||||
viewInfo.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, &resource.view), "vkCreateImageView(texture)");
|
||||
|
||||
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
|
||||
resource.format = format;
|
||||
resource.textureExternalIndex = texture.GetExternalIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::UploadLevel0(TextureResource& resource,
|
||||
const MG_State::GLState::TextureObjectMipmap& mipmapTexture,
|
||||
TextureUploadTarget level0Target, SizeT byteSize) {
|
||||
auto& mutableTexture = const_cast<MG_State::GLState::TextureObjectMipmap&>(mipmapTexture);
|
||||
const void* source = mutableTexture.MapMipmapData(level0Target, 0);
|
||||
if (source == nullptr || byteSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkBuffer stagingBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = byteSize;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
VK_VERIFY(vkCreateBuffer(m_device, &bufferInfo, nullptr, &stagingBuffer), "vkCreateBuffer(staging texture)");
|
||||
|
||||
VkMemoryRequirements requirements{};
|
||||
vkGetBufferMemoryRequirements(m_device, stagingBuffer, &requirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = requirements.size;
|
||||
allocInfo.memoryTypeIndex =
|
||||
FindMemoryType(requirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &stagingMemory), "vkAllocateMemory(staging texture)");
|
||||
VK_VERIFY(vkBindBufferMemory(m_device, stagingBuffer, stagingMemory, 0), "vkBindBufferMemory(staging texture)");
|
||||
|
||||
void* mapped = nullptr;
|
||||
VK_VERIFY(vkMapMemory(m_device, stagingMemory, 0, byteSize, 0, &mapped), "vkMapMemory(staging texture)");
|
||||
std::memcpy(mapped, source, byteSize);
|
||||
vkUnmapMemory(m_device, stagingMemory);
|
||||
|
||||
const Bool ok = ExecuteImmediate([&](VkCommandBuffer commandBuffer) {
|
||||
VkImageMemoryBarrier toTransferDst{};
|
||||
toTransferDst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toTransferDst.srcAccessMask = 0;
|
||||
toTransferDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toTransferDst.oldLayout = resource.layout;
|
||||
toTransferDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransferDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransferDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransferDst.image = resource.image;
|
||||
toTransferDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
toTransferDst.subresourceRange.baseMipLevel = 0;
|
||||
toTransferDst.subresourceRange.levelCount = 1;
|
||||
toTransferDst.subresourceRange.baseArrayLayer = 0;
|
||||
toTransferDst.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &toTransferDst);
|
||||
|
||||
VkBufferImageCopy copy{};
|
||||
copy.bufferOffset = 0;
|
||||
copy.bufferRowLength = 0;
|
||||
copy.bufferImageHeight = 0;
|
||||
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy.imageSubresource.mipLevel = 0;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageOffset = {0, 0, 0};
|
||||
copy.imageExtent = {resource.extent.width, resource.extent.height, 1};
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, resource.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||
©);
|
||||
|
||||
VkImageMemoryBarrier toSampled{};
|
||||
toSampled.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toSampled.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toSampled.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toSampled.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toSampled.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
toSampled.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toSampled.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toSampled.image = resource.image;
|
||||
toSampled.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
toSampled.subresourceRange.baseMipLevel = 0;
|
||||
toSampled.subresourceRange.levelCount = 1;
|
||||
toSampled.subresourceRange.baseArrayLayer = 0;
|
||||
toSampled.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
||||
0, nullptr, 0, nullptr, 1, &toSampled);
|
||||
});
|
||||
|
||||
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(m_device, stagingMemory, nullptr);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
resource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::ExecuteImmediate(const std::function<void(VkCommandBuffer)>& recorder) const {
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.commandPool = m_commandPool;
|
||||
@@ -150,73 +335,310 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer),
|
||||
"VkTextureSamplerManager::UploadFallbackTexture, vkAllocateCommandBuffers");
|
||||
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer), "vkAllocateCommandBuffers(texture)");
|
||||
|
||||
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");
|
||||
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
|
||||
|
||||
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);
|
||||
recorder(commandBuffer);
|
||||
|
||||
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");
|
||||
VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture)");
|
||||
|
||||
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");
|
||||
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE), "vkQueueSubmit(texture)");
|
||||
VK_VERIFY(vkQueueWaitIdle(m_graphicsQueue), "vkQueueWaitIdle(texture)");
|
||||
|
||||
vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
void VkTextureSamplerManager::DestroyTextureResource(TextureResource& resource) const {
|
||||
if (m_device != VK_NULL_HANDLE && resource.view != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(m_device, resource.view, nullptr);
|
||||
}
|
||||
if (m_device != VK_NULL_HANDLE && resource.image != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(m_device, resource.image, nullptr);
|
||||
}
|
||||
if (m_device != VK_NULL_HANDLE && resource.memory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, resource.memory, nullptr);
|
||||
}
|
||||
resource.view = VK_NULL_HANDLE;
|
||||
resource.image = VK_NULL_HANDLE;
|
||||
resource.memory = VK_NULL_HANDLE;
|
||||
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
resource.extent = {0, 0};
|
||||
resource.format = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::ResolveLevel0(const MG_State::GLState::ITextureObject& texture,
|
||||
TextureUploadTarget& outTarget, IntVec3& outTexelSize,
|
||||
SizeT& outByteSize) {
|
||||
const auto* mipTexture = dynamic_cast<const MG_State::GLState::TextureObjectMipmap*>(&texture);
|
||||
if (!mipTexture) {
|
||||
return false;
|
||||
}
|
||||
const auto& targets = texture.GetUploadTargets();
|
||||
if (targets.empty()) {
|
||||
return false;
|
||||
}
|
||||
outTarget = targets.front();
|
||||
outTexelSize = mipTexture->GetMipmapTexelSize(outTarget, 0);
|
||||
outByteSize = mipTexture->GetMipmapByteSize(outTarget, 0);
|
||||
return outTexelSize.x() > 0 && outTexelSize.y() > 0 && outByteSize > 0;
|
||||
}
|
||||
|
||||
VkFormat VkTextureSamplerManager::ResolveTextureFormat(TextureInternalFormat format) {
|
||||
switch (format) {
|
||||
case TextureInternalFormat::RGBA:
|
||||
case TextureInternalFormat::RGBA8:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case TextureInternalFormat::SRGB8Alpha8:
|
||||
return VK_FORMAT_R8G8B8A8_SRGB;
|
||||
default:
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 VkTextureSamplerManager::FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const {
|
||||
VkPhysicalDeviceMemoryProperties memProperties{};
|
||||
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);
|
||||
for (Uint32 i = 0; i < memProperties.memoryTypeCount; ++i) {
|
||||
if ((typeFilter & (1u << i)) != 0 &&
|
||||
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
MOBILEGL_ASSERT(false, "VkTextureSamplerManager::FindMemoryType failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
VkSampler VkTextureSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) {
|
||||
const Uint64 key = BuildSamplerKey(sampler.GetExternalIndex(), sampler.GetVersion());
|
||||
auto it = m_samplers.find(key);
|
||||
if (it != m_samplers.end()) {
|
||||
return it->second.handle;
|
||||
}
|
||||
|
||||
VkSamplerCreateInfo samplerInfo{};
|
||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
samplerInfo.magFilter = ToVkFilter(sampler.GetMagFilter());
|
||||
samplerInfo.minFilter = ToVkFilter(sampler.GetMinFilter());
|
||||
samplerInfo.mipmapMode = ToVkMipmapMode(sampler.GetMipmapMode());
|
||||
samplerInfo.addressModeU = ToVkAddressMode(sampler.GetWrapS());
|
||||
samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT());
|
||||
samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR());
|
||||
samplerInfo.mipLodBias = sampler.GetLodBias();
|
||||
samplerInfo.anisotropyEnable = VK_FALSE;
|
||||
samplerInfo.maxAnisotropy = 1.0f;
|
||||
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
|
||||
samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc());
|
||||
samplerInfo.minLod = sampler.GetMinLod();
|
||||
samplerInfo.maxLod = sampler.GetMaxLod();
|
||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
samplerInfo.unnormalizedCoordinates = VK_FALSE;
|
||||
|
||||
VkSampler vkSampler = VK_NULL_HANDLE;
|
||||
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &vkSampler), "vkCreateSampler(texture)");
|
||||
|
||||
SamplerCacheEntry entry{};
|
||||
entry.handle = vkSampler;
|
||||
entry.externalIndex = sampler.GetExternalIndex();
|
||||
entry.version = sampler.GetVersion();
|
||||
m_samplers[key] = entry;
|
||||
return vkSampler;
|
||||
}
|
||||
|
||||
VkFilter VkTextureSamplerManager::ToVkFilter(SamplerFilterMode mode) {
|
||||
return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
|
||||
}
|
||||
|
||||
VkSamplerMipmapMode VkTextureSamplerManager::ToVkMipmapMode(SamplerMipmapMode mode) {
|
||||
switch (mode) {
|
||||
case SamplerMipmapMode::Nearest:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMipmapMode::Linear:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
case SamplerMipmapMode::None:
|
||||
default:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerAddressMode VkTextureSamplerManager::ToVkAddressMode(SamplerWrapMode mode) {
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::ClampToEdge:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case SamplerWrapMode::MirroredRepeat:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
case SamplerWrapMode::Repeat:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case SamplerWrapMode::ClampToBorder:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
case SamplerWrapMode::MirrorClampToEdge:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
default:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
VkCompareOp VkTextureSamplerManager::ToVkCompareOp(SamplerCompareFunc func) {
|
||||
switch (func) {
|
||||
case SamplerCompareFunc::Never:
|
||||
return VK_COMPARE_OP_NEVER;
|
||||
case SamplerCompareFunc::Less:
|
||||
return VK_COMPARE_OP_LESS;
|
||||
case SamplerCompareFunc::Equal:
|
||||
return VK_COMPARE_OP_EQUAL;
|
||||
case SamplerCompareFunc::LessEqual:
|
||||
return VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
case SamplerCompareFunc::Greater:
|
||||
return VK_COMPARE_OP_GREATER;
|
||||
case SamplerCompareFunc::NotEqual:
|
||||
return VK_COMPARE_OP_NOT_EQUAL;
|
||||
case SamplerCompareFunc::GreaterEqual:
|
||||
return VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
case SamplerCompareFunc::Always:
|
||||
default:
|
||||
return VK_COMPARE_OP_ALWAYS;
|
||||
}
|
||||
}
|
||||
|
||||
Bool VkTextureSamplerManager::UploadFallbackTexture() {
|
||||
const Uint32 rgba = 0xFFFFFFFFu;
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent = {1, 1, 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), "vkCreateImage(fallback)");
|
||||
|
||||
VkMemoryRequirements imageMemReq{};
|
||||
vkGetImageMemoryRequirements(m_device, m_fallbackImage, &imageMemReq);
|
||||
|
||||
VkMemoryAllocateInfo imageAllocInfo{};
|
||||
imageAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
imageAllocInfo.allocationSize = imageMemReq.size;
|
||||
imageAllocInfo.memoryTypeIndex = FindMemoryType(imageMemReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_VERIFY(vkAllocateMemory(m_device, &imageAllocInfo, nullptr, &m_fallbackImageMemory),
|
||||
"vkAllocateMemory(fallback)");
|
||||
VK_VERIFY(vkBindImageMemory(m_device, m_fallbackImage, m_fallbackImageMemory, 0), "vkBindImageMemory(fallback)");
|
||||
|
||||
VkBuffer stagingBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = sizeof(rgba);
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
VK_VERIFY(vkCreateBuffer(m_device, &bufferInfo, nullptr, &stagingBuffer), "vkCreateBuffer(fallback)");
|
||||
|
||||
VkMemoryRequirements stagingMemReq{};
|
||||
vkGetBufferMemoryRequirements(m_device, stagingBuffer, &stagingMemReq);
|
||||
|
||||
VkMemoryAllocateInfo stagingAllocInfo{};
|
||||
stagingAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
stagingAllocInfo.allocationSize = stagingMemReq.size;
|
||||
stagingAllocInfo.memoryTypeIndex =
|
||||
FindMemoryType(stagingMemReq.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
VK_VERIFY(vkAllocateMemory(m_device, &stagingAllocInfo, nullptr, &stagingMemory), "vkAllocateMemory(fallback)");
|
||||
VK_VERIFY(vkBindBufferMemory(m_device, stagingBuffer, stagingMemory, 0), "vkBindBufferMemory(fallback)");
|
||||
|
||||
void* mapped = nullptr;
|
||||
VK_VERIFY(vkMapMemory(m_device, stagingMemory, 0, sizeof(rgba), 0, &mapped), "vkMapMemory(fallback)");
|
||||
std::memcpy(mapped, &rgba, sizeof(rgba));
|
||||
vkUnmapMemory(m_device, stagingMemory);
|
||||
|
||||
const Bool uploadOk = ExecuteImmediate([&](VkCommandBuffer commandBuffer) {
|
||||
VkImageMemoryBarrier toTransferDst{};
|
||||
toTransferDst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toTransferDst.srcAccessMask = 0;
|
||||
toTransferDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toTransferDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
toTransferDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransferDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransferDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransferDst.image = m_fallbackImage;
|
||||
toTransferDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
toTransferDst.subresourceRange.baseMipLevel = 0;
|
||||
toTransferDst.subresourceRange.levelCount = 1;
|
||||
toTransferDst.subresourceRange.baseArrayLayer = 0;
|
||||
toTransferDst.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &toTransferDst);
|
||||
|
||||
VkBufferImageCopy copy{};
|
||||
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy.imageSubresource.mipLevel = 0;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageExtent = {1, 1, 1};
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, m_fallbackImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||
©);
|
||||
|
||||
VkImageMemoryBarrier toSampled{};
|
||||
toSampled.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toSampled.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toSampled.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toSampled.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toSampled.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
toSampled.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toSampled.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toSampled.image = m_fallbackImage;
|
||||
toSampled.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
toSampled.subresourceRange.baseMipLevel = 0;
|
||||
toSampled.subresourceRange.levelCount = 1;
|
||||
toSampled.subresourceRange.baseArrayLayer = 0;
|
||||
toSampled.subresourceRange.layerCount = 1;
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
||||
0, nullptr, 0, nullptr, 1, &toSampled);
|
||||
});
|
||||
|
||||
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(m_device, stagingMemory, nullptr);
|
||||
if (!uploadOk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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), "vkCreateImageView(fallback)");
|
||||
|
||||
VkSamplerCreateInfo samplerInfo{};
|
||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
samplerInfo.magFilter = VK_FILTER_NEAREST;
|
||||
samplerInfo.minFilter = VK_FILTER_NEAREST;
|
||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.compareEnable = VK_FALSE;
|
||||
samplerInfo.minLod = 0.0f;
|
||||
samplerInfo.maxLod = 0.0f;
|
||||
samplerInfo.maxAnisotropy = 1.0f;
|
||||
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &m_fallbackSampler), "vkCreateSampler(fallback)");
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -10,35 +10,79 @@
|
||||
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <MG_State/GLState/SamplerState/SamplerObject.h>
|
||||
#include <MG_State/GLState/TextureState/TextureObject.h>
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
class ITextureObject;
|
||||
class SamplerObject;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
Bool Initialize(const InitInfo& initInfo);
|
||||
void Shutdown();
|
||||
|
||||
Bool GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const;
|
||||
Bool SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture,
|
||||
const MG_State::GLState::SamplerObject* samplerOverride,
|
||||
VkDescriptorImageInfo& outImageInfo);
|
||||
|
||||
private:
|
||||
struct TextureResource {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkExtent2D extent = {0, 0};
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
Uint textureExternalIndex = 0;
|
||||
};
|
||||
|
||||
struct SamplerCacheEntry {
|
||||
VkSampler handle = VK_NULL_HANDLE;
|
||||
Uint externalIndex = 0;
|
||||
Uint16 version = 0;
|
||||
};
|
||||
|
||||
Bool EnsureTextureSynced(TextureResource& resource, const MG_State::GLState::ITextureObject& texture);
|
||||
Bool EnsureTextureResource(TextureResource& resource, const MG_State::GLState::ITextureObject& texture,
|
||||
TextureUploadTarget level0Target, const IntVec3& texelSize, SizeT byteSize);
|
||||
Bool UploadLevel0(TextureResource& resource, const MG_State::GLState::TextureObjectMipmap& mipmapTexture,
|
||||
TextureUploadTarget level0Target, SizeT byteSize);
|
||||
Bool ExecuteImmediate(const std::function<void(VkCommandBuffer)>& recorder) const;
|
||||
void DestroyTextureResource(TextureResource& resource) const;
|
||||
static Bool ResolveLevel0(const MG_State::GLState::ITextureObject& texture, TextureUploadTarget& outTarget,
|
||||
IntVec3& outTexelSize, SizeT& outByteSize);
|
||||
static VkFormat ResolveTextureFormat(TextureInternalFormat format);
|
||||
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
|
||||
|
||||
VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler);
|
||||
static VkFilter ToVkFilter(SamplerFilterMode mode);
|
||||
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
||||
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
||||
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
||||
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;
|
||||
|
||||
UnorderedMap<Uint, TextureResource> m_textureResources;
|
||||
UnorderedMap<Uint64, SamplerCacheEntry> m_samplers;
|
||||
|
||||
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
|
||||
|
||||
@@ -121,20 +121,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
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, 16, 64, 4 * 1024 * 1024,
|
||||
m_textureSamplerManager.get())) {
|
||||
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
|
||||
m_uniformDescriptorBinder.reset();
|
||||
}
|
||||
m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config);
|
||||
m_framebufferManager = MakeUnique<VkFramebufferManager>();
|
||||
if (!m_framebufferManager->Initialize({m_device, m_physicalDevice.handle})) {
|
||||
MGLOG_E("VkFramebufferManager initialization failed. Offscreen FBO clear path is disabled.");
|
||||
m_framebufferManager.reset();
|
||||
}
|
||||
m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>();
|
||||
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
|
||||
m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
|
||||
m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024,
|
||||
m_textureSamplerManager.get(),
|
||||
m_framebufferManager.get())) {
|
||||
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
|
||||
m_uniformDescriptorBinder.reset();
|
||||
}
|
||||
m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config);
|
||||
|
||||
PrepareDemoPipeline();
|
||||
CreateFrameContexts();
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace MobileGL {
|
||||
Int GetUniformMaxLength() const { return m_uniformNameMaxLength; }
|
||||
Uint GetUniformCount() { return m_activeUniformCount; }
|
||||
Uint GetMaxUniformLocation() const { return m_maxUniformLocation; }
|
||||
Int GetUniformLocation(const String& name) {
|
||||
Int GetUniformLocation(const String& name) const {
|
||||
const auto it = m_uniformLocations.find(name);
|
||||
if (it == m_uniformLocations.end()) return -1;
|
||||
return (Int)it->second;
|
||||
|
||||
Reference in New Issue
Block a user