mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (MG_Backend/DirectVulkan): fix Voxy subgroup and indirect draw sync
- Implement Vulkan subgroup capability querying and expose KHR subgroup getter values. - Fix DirectVulkan memory barriers so GL_COMMAND_BARRIER_BIT makes generated indirect draw commands visible. - Keep Voxy on the DirectVulkan gpu_shader_int64 quad decode path while filtering unsupported optional int64 usage on backends that do not advertise it. - Add MG_Test coverage for subgroup getters, Voxy subgroup/int64 shader probes, command barrier mapping, and indirect draw command layout. - Check for whether driver supports shader subgroup operation, disable on demand, and provide env var `MOBILEGL_DISABLE_SUBGROUP` to explicitly disable subgroup features
This commit is contained in:
@@ -113,6 +113,10 @@ namespace MobileGL {
|
|||||||
struct DynamicBackendParameters {
|
struct DynamicBackendParameters {
|
||||||
SizeT UniformBufferOffsetAlignment = 256;
|
SizeT UniformBufferOffsetAlignment = 256;
|
||||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||||
|
Uint32 SubgroupSize = 0;
|
||||||
|
Uint32 SubgroupSupportedStages = 0;
|
||||||
|
Uint32 SubgroupSupportedFeatures = 0;
|
||||||
|
Bool SubgroupQuadOperationsInAllStages = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class WindowBackend {
|
enum class WindowBackend {
|
||||||
|
|||||||
@@ -19,6 +19,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
|
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
|
||||||
|
|
||||||
|
BackendObject_DirectVulkan::BackendObject_DirectVulkan():
|
||||||
|
m_rendererInfo{
|
||||||
|
.RendererName = "Magma",
|
||||||
|
.BackendName = "Direct (Vulkan)",
|
||||||
|
.ExtraVendor = Nullopt,
|
||||||
|
.RendererGLInfo =
|
||||||
|
{
|
||||||
|
.TargetGLVersion = {3, 3, 0},
|
||||||
|
.TargetGLSLVersion = {4, 6, 0},
|
||||||
|
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
|
||||||
|
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
||||||
|
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||||
|
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
||||||
|
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
||||||
|
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||||
|
E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access,
|
||||||
|
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64},
|
||||||
|
.IsCompatibilityProfile = false
|
||||||
|
},
|
||||||
|
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}} {}
|
||||||
|
|
||||||
Bool BackendObject_DirectVulkan::InitWindowSurface() {
|
Bool BackendObject_DirectVulkan::InitWindowSurface() {
|
||||||
if (!m_windowHandle.Handle) {
|
if (!m_windowHandle.Handle) {
|
||||||
MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null");
|
MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null");
|
||||||
@@ -47,8 +68,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetPhysicalDevice().properties);
|
const auto& physicalDevice = pVulkanRenderer->GetPhysicalDevice();
|
||||||
|
if (!MG_Util::BackendLoader::QueryVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetInstance(),
|
||||||
|
physicalDevice.handle)) {
|
||||||
|
MGLOG_W("DirectVulkan: failed to query extended Vulkan capabilities, using basic properties");
|
||||||
|
MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, physicalDevice.properties);
|
||||||
|
}
|
||||||
UpdateDynamicBackendParameters();
|
UpdateDynamicBackendParameters();
|
||||||
|
UpdateAdvertisedExtensions();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,27 +140,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
|
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
|
||||||
static RendererInfo RendererInfo = {
|
return m_rendererInfo;
|
||||||
.RendererName = "Magma", // Renderer Name
|
|
||||||
.BackendName = "Direct (Vulkan)", // Backend Name
|
|
||||||
.ExtraVendor = Nullopt, // Extra vendor
|
|
||||||
.RendererGLInfo =
|
|
||||||
{
|
|
||||||
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
|
|
||||||
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
|
||||||
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
|
|
||||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
|
||||||
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
|
||||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
|
||||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access,
|
|
||||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64},
|
|
||||||
.IsCompatibilityProfile = false // Is Compatibility Profile
|
|
||||||
},
|
|
||||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
|
|
||||||
};
|
|
||||||
return RendererInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String BackendObject_DirectVulkan::GetBackendAPIVersionString() const {
|
String BackendObject_DirectVulkan::GetBackendAPIVersionString() const {
|
||||||
@@ -214,11 +221,81 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return m_dynamicParameters;
|
return m_dynamicParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkan::ApplyVulkanCapabilitiesForTesting(
|
||||||
|
const MG_External::VulkanCapabilities& capabilities) {
|
||||||
|
m_vulkanCaps = capabilities;
|
||||||
|
UpdateDynamicBackendParameters();
|
||||||
|
UpdateAdvertisedExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkan::UpdateAdvertisedExtensions() {
|
||||||
|
auto& extensions = m_rendererInfo.RendererGLInfo.Extensions;
|
||||||
|
extensions.erase(std::remove(extensions.begin(), extensions.end(), E_GL_KHR_shader_subgroup),
|
||||||
|
extensions.end());
|
||||||
|
|
||||||
|
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
||||||
|
extensions.push_back(E_GL_KHR_shader_subgroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() {
|
void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() {
|
||||||
|
const auto mapShaderStages = [](Uint32 vkStages) {
|
||||||
|
Uint32 glStages = 0;
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_VERTEX_BIT) != 0) glStages |= GL_VERTEX_SHADER_BIT;
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0) glStages |= GL_TESS_CONTROL_SHADER_BIT;
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0) {
|
||||||
|
glStages |= GL_TESS_EVALUATION_SHADER_BIT;
|
||||||
|
}
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_GEOMETRY_BIT) != 0) glStages |= GL_GEOMETRY_SHADER_BIT;
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_FRAGMENT_BIT) != 0) glStages |= GL_FRAGMENT_SHADER_BIT;
|
||||||
|
if ((vkStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0) glStages |= GL_COMPUTE_SHADER_BIT;
|
||||||
|
return glStages;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto mapSubgroupFeatures = [](Uint32 vkFeatures) {
|
||||||
|
Uint32 glFeatures = 0;
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_BASIC_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_VOTE_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_VOTE_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_BALLOT_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR;
|
||||||
|
}
|
||||||
|
if ((vkFeatures & VK_SUBGROUP_FEATURE_QUAD_BIT) != 0) {
|
||||||
|
glFeatures |= GL_SUBGROUP_FEATURE_QUAD_BIT_KHR;
|
||||||
|
}
|
||||||
|
return glFeatures;
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull;
|
static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull;
|
||||||
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
|
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
|
||||||
m_dynamicParameters.MaxShaderStorageBlockSize =
|
m_dynamicParameters.MaxShaderStorageBlockSize =
|
||||||
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
|
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
|
||||||
|
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
||||||
|
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
|
||||||
|
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
|
||||||
|
m_dynamicParameters.SubgroupSupportedFeatures = mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
|
||||||
|
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
|
||||||
|
} else {
|
||||||
|
m_dynamicParameters.SubgroupSize = 0;
|
||||||
|
m_dynamicParameters.SubgroupSupportedStages = 0;
|
||||||
|
m_dynamicParameters.SubgroupSupportedFeatures = 0;
|
||||||
|
m_dynamicParameters.SubgroupQuadOperationsInAllStages = false;
|
||||||
|
}
|
||||||
if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) {
|
if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) {
|
||||||
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
|
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
|
||||||
m_vulkanCaps.MaxShaderStorageBlockSize,
|
m_vulkanCaps.MaxShaderStorageBlockSize,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class BackendObject_DirectVulkan : public BackendObject {
|
class BackendObject_DirectVulkan : public BackendObject {
|
||||||
public:
|
public:
|
||||||
|
BackendObject_DirectVulkan();
|
||||||
~BackendObject_DirectVulkan() override;
|
~BackendObject_DirectVulkan() override;
|
||||||
|
|
||||||
void Initialize() override;
|
void Initialize() override;
|
||||||
@@ -30,12 +31,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
|
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
|
||||||
const DynamicBackendParameters& GetDynamicParameters() const override;
|
const DynamicBackendParameters& GetDynamicParameters() const override;
|
||||||
BackendType GetBackendType() const override;
|
BackendType GetBackendType() const override;
|
||||||
|
void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateAdvertisedExtensions();
|
||||||
void UpdateDynamicBackendParameters();
|
void UpdateDynamicBackendParameters();
|
||||||
|
|
||||||
Bool m_initialized = false;
|
Bool m_initialized = false;
|
||||||
DynamicBackendParameters m_dynamicParameters;
|
DynamicBackendParameters m_dynamicParameters;
|
||||||
MG_External::VulkanCapabilities m_vulkanCaps;
|
MG_External::VulkanCapabilities m_vulkanCaps;
|
||||||
|
RendererInfo m_rendererInfo;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -2920,16 +2920,7 @@ void main() {
|
|||||||
vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast<VkDeviceSize>(indirect));
|
vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast<VkDeviceSize>(indirect));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::MemoryBarrier(GLbitfield barriers) {
|
VkMemoryBarrier VulkanRenderer::BuildMemoryBarrierForGlBarriers(GLbitfield barriers) {
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
|
||||||
if (!frame.isCommandRecording) {
|
|
||||||
m_frameContext.BeginCommandRecording();
|
|
||||||
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
|
||||||
}
|
|
||||||
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
|
|
||||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkMemoryBarrier memoryBarrier{};
|
VkMemoryBarrier memoryBarrier{};
|
||||||
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||||
memoryBarrier.srcAccessMask =
|
memoryBarrier.srcAccessMask =
|
||||||
@@ -2945,6 +2936,24 @@ void main() {
|
|||||||
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT |
|
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT |
|
||||||
VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
|
VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
|
||||||
|
|
||||||
|
if ((barriers & GL_COMMAND_BARRIER_BIT) != 0) {
|
||||||
|
memoryBarrier.dstAccessMask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
||||||
|
}
|
||||||
|
return memoryBarrier;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::MemoryBarrier(GLbitfield barriers) {
|
||||||
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
if (!frame.isCommandRecording) {
|
||||||
|
m_frameContext.BeginCommandRecording();
|
||||||
|
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
|
||||||
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkMemoryBarrier memoryBarrier = BuildMemoryBarrierForGlBarriers(barriers);
|
||||||
|
|
||||||
MGLOG_D("DirectVulkan: glMemoryBarrier(0x%x)", static_cast<Uint32>(barriers));
|
MGLOG_D("DirectVulkan: glMemoryBarrier(0x%x)", static_cast<Uint32>(barriers));
|
||||||
vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
|
||||||
@@ -5331,6 +5340,10 @@ void main() {
|
|||||||
return m_physicalDevice;
|
return m_physicalDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkInstance VulkanRenderer::GetInstance() const {
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::IsDrawIndirectCountExtensionEnabled() const {
|
Bool VulkanRenderer::IsDrawIndirectCountExtensionEnabled() const {
|
||||||
return m_drawIndirectCountExtensionEnabled;
|
return m_drawIndirectCountExtensionEnabled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
|
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
|
||||||
void DispatchComputeIndirect(GLintptr indirect);
|
void DispatchComputeIndirect(GLintptr indirect);
|
||||||
void MemoryBarrier(GLbitfield barriers);
|
void MemoryBarrier(GLbitfield barriers);
|
||||||
|
static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers);
|
||||||
void DrawArrays(const DrawCmd& payload);
|
void DrawArrays(const DrawCmd& payload);
|
||||||
void DrawElements(const DrawIndexedCmd& payload);
|
void DrawElements(const DrawIndexedCmd& payload);
|
||||||
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
||||||
@@ -151,6 +152,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void Present();
|
void Present();
|
||||||
|
|
||||||
const PhysicalDevice& GetPhysicalDevice() const;
|
const PhysicalDevice& GetPhysicalDevice() const;
|
||||||
|
VkInstance GetInstance() const;
|
||||||
Bool IsDrawIndirectCountExtensionEnabled() const;
|
Bool IsDrawIndirectCountExtensionEnabled() const;
|
||||||
|
|
||||||
void RecreateSwapchain();
|
void RecreateSwapchain();
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str());
|
MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str());
|
||||||
return (const GLubyte*)shadingLanguageVersion.c_str();
|
return (const GLubyte*)shadingLanguageVersion.c_str();
|
||||||
case GL_EXTENSIONS:
|
case GL_EXTENSIONS:
|
||||||
if (extensionsString.empty()) {
|
extensionsString.clear();
|
||||||
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
|
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
|
||||||
extensionsString += MG_Util::ConvertGLExtToString(ext);
|
if (!extensionsString.empty()) {
|
||||||
extensionsString += " ";
|
extensionsString += " ";
|
||||||
}
|
}
|
||||||
extensionsString.pop_back();
|
extensionsString += MG_Util::ConvertGLExtToString(ext);
|
||||||
}
|
}
|
||||||
return (const GLubyte*)extensionsString.c_str();
|
return (const GLubyte*)extensionsString.c_str();
|
||||||
default:
|
default:
|
||||||
@@ -108,14 +108,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Vector<String> extStrings;
|
static Vector<String> extStrings;
|
||||||
static Bool initialized = false;
|
extStrings.clear();
|
||||||
if (!initialized) {
|
|
||||||
extStrings.reserve(exts.size());
|
extStrings.reserve(exts.size());
|
||||||
for (const auto& ext : exts) {
|
for (const auto& ext : exts) {
|
||||||
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
|
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
|
||||||
}
|
}
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (const GLubyte*)extStrings[index].c_str();
|
return (const GLubyte*)extStrings[index].c_str();
|
||||||
}
|
}
|
||||||
@@ -174,6 +171,15 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*data = static_cast<GLint64>(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize);
|
*data = static_cast<GLint64>(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case GL_SUBGROUP_SIZE_KHR:
|
||||||
|
case GL_SUBGROUP_SUPPORTED_STAGES_KHR:
|
||||||
|
case GL_SUBGROUP_SUPPORTED_FEATURES_KHR:
|
||||||
|
case GL_SUBGROUP_QUAD_ALL_STAGES_KHR: {
|
||||||
|
GLint params = 0;
|
||||||
|
GetIntegerv(pname, ¶ms);
|
||||||
|
*data = static_cast<GLint64>(params);
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
*data = 0;
|
*data = 0;
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
@@ -200,6 +206,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
|
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
|
||||||
|
const auto& dynamicParameters = activeBackendObject->GetDynamicParameters();
|
||||||
|
|
||||||
switch (pname) {
|
switch (pname) {
|
||||||
case GL_ACTIVE_TEXTURE:
|
case GL_ACTIVE_TEXTURE:
|
||||||
@@ -277,6 +284,18 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_COMPRESSED_TEXTURE_FORMATS:
|
case GL_COMPRESSED_TEXTURE_FORMATS:
|
||||||
*params = 0; // TODO
|
*params = 0; // TODO
|
||||||
break;
|
break;
|
||||||
|
case GL_SUBGROUP_SIZE_KHR:
|
||||||
|
*params = static_cast<GLint>(dynamicParameters.SubgroupSize);
|
||||||
|
break;
|
||||||
|
case GL_SUBGROUP_SUPPORTED_STAGES_KHR:
|
||||||
|
*params = static_cast<GLint>(dynamicParameters.SubgroupSupportedStages);
|
||||||
|
break;
|
||||||
|
case GL_SUBGROUP_SUPPORTED_FEATURES_KHR:
|
||||||
|
*params = static_cast<GLint>(dynamicParameters.SubgroupSupportedFeatures);
|
||||||
|
break;
|
||||||
|
case GL_SUBGROUP_QUAD_ALL_STAGES_KHR:
|
||||||
|
*params = dynamicParameters.SubgroupQuadOperationsInAllStages ? GL_TRUE : GL_FALSE;
|
||||||
|
break;
|
||||||
case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
|
case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
|
||||||
*params = 16; // TODO: use backend value
|
*params = 16; // TODO: use backend value
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
||||||
|
|
||||||
const SizeT srcRowSize = static_cast<SizeT>(width) * internalBpp;
|
const SizeT srcRowSize = static_cast<SizeT>(width) * internalBpp;
|
||||||
const SizeT srcStride = (srcRowSize + unpackParams.Alignment - 1) & ~(unpackParams.Alignment - 1);
|
const SizeT srcStride = srcRowSize;
|
||||||
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
||||||
|
|
||||||
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include "Includes.h"
|
#include "Includes.h"
|
||||||
#include "Init.h"
|
#include "Init.h"
|
||||||
#include "MG_Backend/DirectVulkan/DirectVulkanResourceState.h"
|
#include "MG_Backend/DirectVulkan/DirectVulkanResourceState.h"
|
||||||
|
#include "MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h"
|
||||||
|
#include "MG_Backend/BackendObjects.h"
|
||||||
#include "MG_Impl/GLImpl/Getter/GL_Getter.h"
|
#include "MG_Impl/GLImpl/Getter/GL_Getter.h"
|
||||||
#include "MG_Impl/GLImpl/Program/GL_Program.h"
|
#include "MG_Impl/GLImpl/Program/GL_Program.h"
|
||||||
#include "MG_State/GLState/Core.h"
|
#include "MG_State/GLState/Core.h"
|
||||||
@@ -118,6 +120,80 @@ TEST_F(ProgramTest, CompileFragment) {
|
|||||||
CompileShader(fs);
|
CompileShader(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, CompileVoxySubgroupProbeShader) {
|
||||||
|
char infoLog[1024] = "";
|
||||||
|
const char* csSrc = R"(#version 430
|
||||||
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic : require
|
||||||
|
layout(local_size_x=32) in;
|
||||||
|
void main() {
|
||||||
|
uint a = subgroupExclusiveAdd(gl_LocalInvocationIndex);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
GLuint cs = CreateShader(GL_COMPUTE_SHADER);
|
||||||
|
ShaderSource(cs, 1, &csSrc, nullptr);
|
||||||
|
CompileShader(cs);
|
||||||
|
|
||||||
|
GLint compileStatus = GL_FALSE;
|
||||||
|
GetShaderiv(cs, GL_COMPILE_STATUS, &compileStatus);
|
||||||
|
GetShaderInfoLog(cs, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
EXPECT_EQ(compileStatus, GL_TRUE) << infoLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, CompileVoxyGpuShaderInt64QuadDecode) {
|
||||||
|
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
|
||||||
|
MG_Backend::pActiveBackendObject = MakeUnique<MG_Backend::DirectVulkan::BackendObject_DirectVulkan>();
|
||||||
|
|
||||||
|
char infoLog[2048] = "";
|
||||||
|
const char* vsSrc = R"(#version 460 core
|
||||||
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
|
||||||
|
#ifdef GL_ARB_gpu_shader_int64
|
||||||
|
#define Quad uint64_t
|
||||||
|
#define Eu32(data, amountBits, shift) (uint((data)>>(shift))&((1u<<(amountBits))-1))
|
||||||
|
|
||||||
|
vec3 extractPos(uint64_t quad) {
|
||||||
|
return vec3(Eu32(quad, 5, 21), Eu32(quad, 5, 16), Eu32(quad, 5, 11));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint extractStateId(uint64_t quad) {
|
||||||
|
return Eu32(quad, 16, 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint extractBiomeId(uint64_t quad) {
|
||||||
|
return Eu32(quad, 9, 46);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error GL_ARB_gpu_shader_int64 should select Voxy native quad decode path
|
||||||
|
#endif
|
||||||
|
|
||||||
|
layout(std430, binding = 1) readonly buffer QuadBuffer {
|
||||||
|
Quad quadData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) flat out uvec4 interData;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint64_t quad = quadData[uint(gl_VertexID) >> 2];
|
||||||
|
vec3 pos = extractPos(quad);
|
||||||
|
interData = uvec4(extractStateId(quad), extractBiomeId(quad), uint(pos.x), uint(pos.y));
|
||||||
|
gl_Position = vec4(pos * (1.0 / 32.0), 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
GLuint vs = CreateShader(GL_VERTEX_SHADER);
|
||||||
|
ShaderSource(vs, 1, &vsSrc, nullptr);
|
||||||
|
CompileShader(vs);
|
||||||
|
|
||||||
|
GLint compileStatus = GL_FALSE;
|
||||||
|
GetShaderiv(vs, GL_COMPILE_STATUS, &compileStatus);
|
||||||
|
GetShaderInfoLog(vs, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
EXPECT_EQ(compileStatus, GL_TRUE) << infoLog;
|
||||||
|
|
||||||
|
MG_Backend::pActiveBackendObject = Move(previousBackend);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ProgramTest, ShaderSourceKeepsOriginalTextAfterCompile) {
|
TEST_F(ProgramTest, ShaderSourceKeepsOriginalTextAfterCompile) {
|
||||||
const char* part0 = R"(#define HIGHP_OR_DEFAULT highp
|
const char* part0 = R"(#define HIGHP_OR_DEFAULT highp
|
||||||
attribute vec4 Position;
|
attribute vec4 Position;
|
||||||
|
|||||||
@@ -21,9 +21,44 @@
|
|||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h>
|
#include <MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h>
|
||||||
#include <MG_Backend/DirectVulkan/Renderer/VkTextureManager.h>
|
#include <MG_Backend/DirectVulkan/Renderer/VkTextureManager.h>
|
||||||
|
#include <MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h>
|
||||||
|
#include <MG_Util/ShaderTranspiler/ShaderCompiler.h>
|
||||||
|
#include <MG_Util/ShaderTranspiler/ShaderSourceProcessor.h>
|
||||||
#include <MG_Util/Debug/Log.h>
|
#include <MG_Util/Debug/Log.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
class DynamicParameterBackend final : public MobileGL::MG_Backend::BackendObject {
|
||||||
|
public:
|
||||||
|
explicit DynamicParameterBackend(MobileGL::MG_Backend::DynamicBackendParameters params):
|
||||||
|
m_params(params) {}
|
||||||
|
|
||||||
|
void Initialize() override {}
|
||||||
|
MobileGL::Bool InitCapabilities() override { return true; }
|
||||||
|
MobileGL::Bool InitWindowSurface() override { return true; }
|
||||||
|
const MobileGL::RendererInfo& GetRendererInfo() const override { return m_info; }
|
||||||
|
MobileGL::String GetBackendAPIVersionString() const override { return "test"; }
|
||||||
|
const MobileGL::MG_Backend::GlobalBackendFunctionsTable& GetBackendFunctions() const override {
|
||||||
|
return m_functions;
|
||||||
|
}
|
||||||
|
const MobileGL::MG_Backend::DynamicBackendParameters& GetDynamicParameters() const override {
|
||||||
|
return m_params;
|
||||||
|
}
|
||||||
|
MobileGL::BackendType GetBackendType() const override { return MobileGL::BackendType::Unknown; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
MobileGL::MG_Backend::DynamicBackendParameters m_params;
|
||||||
|
MobileGL::MG_Backend::GlobalBackendFunctionsTable m_functions{};
|
||||||
|
MobileGL::RendererInfo m_info{
|
||||||
|
.RendererName = "Test",
|
||||||
|
.BackendName = "DynamicParameterBackend",
|
||||||
|
.ExtraVendor = MobileGL::Nullopt,
|
||||||
|
.RendererGLInfo = {.TargetGLVersion = {3, 3, 0},
|
||||||
|
.TargetGLSLVersion = {4, 6, 0},
|
||||||
|
.Extensions = {},
|
||||||
|
.IsCompatibilityProfile = false},
|
||||||
|
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}};
|
||||||
|
};
|
||||||
|
|
||||||
void SetEnvVar(const char* name, const char* value) {
|
void SetEnvVar(const char* name, const char* value) {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
_putenv_s(name, value);
|
_putenv_s(name, value);
|
||||||
@@ -87,6 +122,137 @@ TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaising
|
|||||||
extensions.end());
|
extensions.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DirectVulkanSanity, AdvertisesSubgroupOnlyWhenVulkanReportsUsableSupport) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||||
|
|
||||||
|
MG_External::VulkanCapabilities unsupportedCaps;
|
||||||
|
unsupportedCaps.SupportsShaderSubgroup = false;
|
||||||
|
unsupportedCaps.SubgroupSize = 32;
|
||||||
|
unsupportedCaps.SubgroupSupportedStages = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
unsupportedCaps.SubgroupSupportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT;
|
||||||
|
backend.ApplyVulkanCapabilitiesForTesting(unsupportedCaps);
|
||||||
|
|
||||||
|
const auto& unsupportedExtensions = backend.GetRendererInfo().RendererGLInfo.Extensions;
|
||||||
|
EXPECT_EQ(std::find(unsupportedExtensions.begin(), unsupportedExtensions.end(), E_GL_KHR_shader_subgroup),
|
||||||
|
unsupportedExtensions.end());
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSize, 0u);
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedStages, 0u);
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedFeatures, 0u);
|
||||||
|
|
||||||
|
MG_External::VulkanCapabilities supportedCaps;
|
||||||
|
supportedCaps.SupportsShaderSubgroup = true;
|
||||||
|
supportedCaps.SubgroupSize = 32;
|
||||||
|
supportedCaps.SubgroupSupportedStages = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
supportedCaps.SubgroupSupportedOperations =
|
||||||
|
VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_QUAD_BIT;
|
||||||
|
supportedCaps.SubgroupQuadOperationsInAllStages = true;
|
||||||
|
backend.ApplyVulkanCapabilitiesForTesting(supportedCaps);
|
||||||
|
|
||||||
|
const auto& supportedExtensions = backend.GetRendererInfo().RendererGLInfo.Extensions;
|
||||||
|
EXPECT_NE(std::find(supportedExtensions.begin(), supportedExtensions.end(), E_GL_KHR_shader_subgroup),
|
||||||
|
supportedExtensions.end());
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSize, 32u);
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedStages,
|
||||||
|
static_cast<Uint32>(GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT));
|
||||||
|
EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedFeatures,
|
||||||
|
static_cast<Uint32>(GL_SUBGROUP_FEATURE_BASIC_BIT_KHR |
|
||||||
|
GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR |
|
||||||
|
GL_SUBGROUP_FEATURE_QUAD_BIT_KHR));
|
||||||
|
EXPECT_TRUE(backend.GetDynamicParameters().SubgroupQuadOperationsInAllStages);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectVulkanSanity, KeepsOptionalGpuShaderInt64BranchForVoxyQuadDecode) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
MG_Backend::pActiveBackendObject = MakeUnique<MG_Backend::DirectVulkan::BackendObject_DirectVulkan>();
|
||||||
|
String source = R"(#version 460 core
|
||||||
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
#ifdef GL_ARB_gpu_shader_int64
|
||||||
|
uint getLowBits(uint64_t v) {
|
||||||
|
return uint(v & uint64_t(0xffu));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error int64 branch should be enabled for DirectVulkan
|
||||||
|
#endif
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(float(getLowBits(uint64_t(0x2au))));
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
MG_Util::ShaderTranspiler::PreprocessShaderSource(ShaderStage::Vertex, source);
|
||||||
|
EXPECT_NE(source.find("#extension GL_ARB_gpu_shader_int64"), String::npos);
|
||||||
|
EXPECT_NE(source.find("GL_ARB_gpu_shader_int64"), String::npos);
|
||||||
|
|
||||||
|
auto shaderResult = MG_Util::ShaderTranspiler::ShaderCompiler::CompileShader({
|
||||||
|
.shaderType = GL_VERTEX_SHADER,
|
||||||
|
.sourceStr = source,
|
||||||
|
.flags = MG_Util::ShaderTranspiler::ShaderCompileBits::CompileForOpenGL,
|
||||||
|
});
|
||||||
|
EXPECT_TRUE(shaderResult) << (shaderResult ? "" : shaderResult.error().log);
|
||||||
|
|
||||||
|
MG_Backend::pActiveBackendObject.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GetterSanity, ReportsKhrSubgroupDynamicParameters) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
|
||||||
|
|
||||||
|
MG_Backend::DynamicBackendParameters params;
|
||||||
|
params.SubgroupSize = 32;
|
||||||
|
params.SubgroupSupportedStages = GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT;
|
||||||
|
params.SubgroupSupportedFeatures = GL_SUBGROUP_FEATURE_BASIC_BIT_KHR |
|
||||||
|
GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR |
|
||||||
|
GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR |
|
||||||
|
GL_SUBGROUP_FEATURE_QUAD_BIT_KHR;
|
||||||
|
params.SubgroupQuadOperationsInAllStages = true;
|
||||||
|
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(params);
|
||||||
|
|
||||||
|
GLint intValue = 0;
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SIZE_KHR, &intValue);
|
||||||
|
EXPECT_EQ(intValue, 32);
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SUPPORTED_STAGES_KHR, &intValue);
|
||||||
|
EXPECT_EQ(intValue, static_cast<GLint>(params.SubgroupSupportedStages));
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SUPPORTED_FEATURES_KHR, &intValue);
|
||||||
|
EXPECT_EQ(intValue, static_cast<GLint>(params.SubgroupSupportedFeatures));
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_QUAD_ALL_STAGES_KHR, &intValue);
|
||||||
|
EXPECT_EQ(intValue, GL_TRUE);
|
||||||
|
|
||||||
|
GLint64 int64Value = 0;
|
||||||
|
MG_Impl::GLImpl::GetInteger64v(GL_SUBGROUP_SUPPORTED_FEATURES_KHR, &int64Value);
|
||||||
|
EXPECT_EQ(int64Value, static_cast<GLint64>(params.SubgroupSupportedFeatures));
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MG_Backend::pActiveBackendObject.reset();
|
||||||
|
MG_State::pGLContext.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectVulkanSanity, CommandMemoryBarrierMakesIndirectDrawCommandsVisible) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
using namespace MobileGL::MG_Backend::DirectVulkan;
|
||||||
|
|
||||||
|
const VkMemoryBarrier commandBarrier =
|
||||||
|
VulkanRenderer::BuildMemoryBarrierForGlBarriers(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT);
|
||||||
|
EXPECT_NE(commandBarrier.dstAccessMask & VK_ACCESS_INDIRECT_COMMAND_READ_BIT, 0u);
|
||||||
|
|
||||||
|
const VkMemoryBarrier storageOnlyBarrier =
|
||||||
|
VulkanRenderer::BuildMemoryBarrierForGlBarriers(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||||
|
EXPECT_EQ(storageOnlyBarrier.dstAccessMask & VK_ACCESS_INDIRECT_COMMAND_READ_BIT, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectVulkanSanity, DrawIndexedIndirectCommandMatchesGlAndVulkanLayout) {
|
||||||
|
using namespace MobileGL::MG_Backend::DirectVulkan;
|
||||||
|
|
||||||
|
EXPECT_EQ(sizeof(DrawIndexedCmdParam), 20u);
|
||||||
|
EXPECT_EQ(offsetof(DrawIndexedCmdParam, indexCount), 0u);
|
||||||
|
EXPECT_EQ(offsetof(DrawIndexedCmdParam, instanceCount), 4u);
|
||||||
|
EXPECT_EQ(offsetof(DrawIndexedCmdParam, firstIndex), 8u);
|
||||||
|
EXPECT_EQ(offsetof(DrawIndexedCmdParam, vertexOffset), 12u);
|
||||||
|
EXPECT_EQ(offsetof(DrawIndexedCmdParam, firstInstance), 16u);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DirectVulkanSanity, UndefinedDepthStencilLayoutUsesDontCareForUnclearedAspects) {
|
TEST(DirectVulkanSanity, UndefinedDepthStencilLayoutUsesDontCareForUnclearedAspects) {
|
||||||
using namespace MobileGL;
|
using namespace MobileGL;
|
||||||
using namespace MobileGL::MG_Backend::DirectVulkan;
|
using namespace MobileGL::MG_Backend::DirectVulkan;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "Includes.h"
|
#include "Includes.h"
|
||||||
#include "Init.h"
|
#include "Init.h"
|
||||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||||
|
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
||||||
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
|
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
|
||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_State/GLState/TextureState/TextureObject.h>
|
#include <MG_State/GLState/TextureState/TextureObject.h>
|
||||||
@@ -66,6 +67,38 @@ TEST_F(TextureTest, TextureStorageAndSubImageModifyNamedObjectOnly) {
|
|||||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TextureTest, BoundTexSubImage2DUsesCompactRowsAfterUnpackProcessing) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
const Uint8 initialPixels[2 * 16] = {};
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 5, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, initialPixels);
|
||||||
|
|
||||||
|
const Uint8 subImageWithGuard[] = {
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
|
101, 102, 103,
|
||||||
|
10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||||
|
201, 202, 203, 204, 205, 206,
|
||||||
|
};
|
||||||
|
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
|
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 1, 0, 3, 2, GL_RGB, GL_UNSIGNED_BYTE, subImageWithGuard);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
const auto* stored = static_cast<const Uint8*>(
|
||||||
|
mipmapObject->MapMipmapData(TextureUploadTarget::Texture2D, 0));
|
||||||
|
|
||||||
|
const Uint8 expected[] = {
|
||||||
|
0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0,
|
||||||
|
0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, 0, 0,
|
||||||
|
};
|
||||||
|
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||||
|
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) {
|
TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) {
|
||||||
GLuint texture = 0;
|
GLuint texture = 0;
|
||||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#include "Loader.h"
|
#include "Loader.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace MobileGL::MG_Util::BackendLoader {
|
namespace MobileGL::MG_Util::BackendLoader {
|
||||||
namespace {
|
namespace {
|
||||||
struct VulkanDynamicFunctions {
|
struct VulkanDynamicFunctions {
|
||||||
@@ -33,6 +36,14 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
|
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool IsShaderSubgroupForcedDisabled() {
|
||||||
|
const char* value = std::getenv("MOBILEGL_DISABLE_SUBGROUP");
|
||||||
|
if (!value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return std::strcmp(value, "true") == 0 || std::strcmp(value, "TRUE") == 0;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
inline Version DecodeApiVersion(uint32_t version) {
|
inline Version DecodeApiVersion(uint32_t version) {
|
||||||
@@ -46,6 +57,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Bool HasUsableShaderSubgroupSupport(const VkPhysicalDeviceSubgroupProperties& subgroupProps) {
|
||||||
|
return subgroupProps.subgroupSize > 0 &&
|
||||||
|
(subgroupProps.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0 &&
|
||||||
|
(subgroupProps.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
Bool QueryVulkanCapabilities(MobileGL::MG_External::VulkanCapabilities& caps, VkInstance instance,
|
Bool QueryVulkanCapabilities(MobileGL::MG_External::VulkanCapabilities& caps, VkInstance instance,
|
||||||
VkPhysicalDevice physicalDevice) {
|
VkPhysicalDevice physicalDevice) {
|
||||||
if (!physicalDevice) {
|
if (!physicalDevice) {
|
||||||
@@ -69,8 +86,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceSubgroupProperties subgroupProps{};
|
||||||
|
subgroupProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
|
||||||
|
|
||||||
VkPhysicalDeviceProperties2 props2{};
|
VkPhysicalDeviceProperties2 props2{};
|
||||||
props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
|
props2.pNext = &subgroupProps;
|
||||||
if (vk.vkGetPhysicalDeviceProperties2) {
|
if (vk.vkGetPhysicalDeviceProperties2) {
|
||||||
vk.vkGetPhysicalDeviceProperties2(physicalDevice, &props2);
|
vk.vkGetPhysicalDeviceProperties2(physicalDevice, &props2);
|
||||||
} else {
|
} else {
|
||||||
@@ -84,6 +105,28 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.DriverVersionString = DecodeDriverVersion(p.driverVersion);
|
caps.DriverVersionString = DecodeDriverVersion(p.driverVersion);
|
||||||
caps.UniformBufferOffsetAlignment = static_cast<int>(p.limits.minUniformBufferOffsetAlignment);
|
caps.UniformBufferOffsetAlignment = static_cast<int>(p.limits.minUniformBufferOffsetAlignment);
|
||||||
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(p.limits.maxStorageBufferRange);
|
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(p.limits.maxStorageBufferRange);
|
||||||
|
const Bool supportsShaderSubgroup = vk.vkGetPhysicalDeviceProperties2 &&
|
||||||
|
HasUsableShaderSubgroupSupport(subgroupProps);
|
||||||
|
const Bool forceDisableShaderSubgroup = IsShaderSubgroupForcedDisabled();
|
||||||
|
caps.SupportsShaderSubgroup = supportsShaderSubgroup && !forceDisableShaderSubgroup;
|
||||||
|
if (caps.SupportsShaderSubgroup) {
|
||||||
|
caps.SubgroupSize = subgroupProps.subgroupSize;
|
||||||
|
caps.SubgroupSupportedStages = subgroupProps.supportedStages;
|
||||||
|
caps.SubgroupSupportedOperations = subgroupProps.supportedOperations;
|
||||||
|
caps.SubgroupQuadOperationsInAllStages = subgroupProps.quadOperationsInAllStages == VK_TRUE;
|
||||||
|
} else {
|
||||||
|
caps.SubgroupSize = 0;
|
||||||
|
caps.SubgroupSupportedStages = 0;
|
||||||
|
caps.SubgroupSupportedOperations = 0;
|
||||||
|
caps.SubgroupQuadOperationsInAllStages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MGLOG_I("Vulkan shader subgroup support: detected=%s advertised=%s size=%u stages=0x%x operations=0x%x",
|
||||||
|
supportsShaderSubgroup ? "true" : "false", caps.SupportsShaderSubgroup ? "true" : "false",
|
||||||
|
subgroupProps.subgroupSize, subgroupProps.supportedStages, subgroupProps.supportedOperations);
|
||||||
|
if (supportsShaderSubgroup && forceDisableShaderSubgroup) {
|
||||||
|
MGLOG_W("Vulkan shader subgroup support forced off by MOBILEGL_DISABLE_SUBGROUP");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -95,5 +138,10 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion);
|
caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion);
|
||||||
caps.UniformBufferOffsetAlignment = static_cast<int>(properties.limits.minUniformBufferOffsetAlignment);
|
caps.UniformBufferOffsetAlignment = static_cast<int>(properties.limits.minUniformBufferOffsetAlignment);
|
||||||
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(properties.limits.maxStorageBufferRange);
|
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(properties.limits.maxStorageBufferRange);
|
||||||
|
caps.SupportsShaderSubgroup = false;
|
||||||
|
caps.SubgroupSize = 0;
|
||||||
|
caps.SubgroupSupportedStages = 0;
|
||||||
|
caps.SubgroupSupportedOperations = 0;
|
||||||
|
caps.SubgroupQuadOperationsInAllStages = false;
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Util::BackendLoader
|
} // namespace MobileGL::MG_Util::BackendLoader
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ namespace MobileGL {
|
|||||||
String DriverVersionString;
|
String DriverVersionString;
|
||||||
Int UniformBufferOffsetAlignment = 256;
|
Int UniformBufferOffsetAlignment = 256;
|
||||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||||
|
Bool SupportsShaderSubgroup = false;
|
||||||
|
Uint32 SubgroupSize = 0;
|
||||||
|
Uint32 SubgroupSupportedStages = 0;
|
||||||
|
Uint32 SubgroupSupportedOperations = 0;
|
||||||
|
Bool SubgroupQuadOperationsInAllStages = false;
|
||||||
};
|
};
|
||||||
} // namespace MG_External
|
} // namespace MG_External
|
||||||
namespace MG_Util::BackendLoader {
|
namespace MG_Util::BackendLoader {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#include "ShaderSourceProcessor.h"
|
#include "ShaderSourceProcessor.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <MG_Backend/BackendObjects.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using MobileGL::SizeT;
|
using MobileGL::SizeT;
|
||||||
@@ -159,6 +161,106 @@ namespace {
|
|||||||
return lineEnd == MobileGL::String::npos ? source.size() : lineEnd + 1;
|
return lineEnd == MobileGL::String::npos ? source.size() : lineEnd + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsExtensionAdvertised(MobileGL::GLExtension extension) {
|
||||||
|
const auto& activeBackendObject = MobileGL::MG_Backend::pActiveBackendObject;
|
||||||
|
if (!activeBackendObject) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& extensions = activeBackendObject->GetRendererInfo().RendererGLInfo.Extensions;
|
||||||
|
return std::find(extensions.begin(), extensions.end(), extension) != extensions.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
MobileGL::String TrimDirectiveToken(const MobileGL::String& token) {
|
||||||
|
SizeT start = 0;
|
||||||
|
while (start < token.size() && std::isspace(static_cast<unsigned char>(token[start]))) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeT end = token.size();
|
||||||
|
while (end > start && std::isspace(static_cast<unsigned char>(token[end - 1]))) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return token.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilterUnsupportedGpuShaderInt64(MobileGL::String& source) {
|
||||||
|
if (IsExtensionAdvertised(MobileGL::E_GL_ARB_gpu_shader_int64)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeT lineStart = 0;
|
||||||
|
while (lineStart < source.size()) {
|
||||||
|
SizeT lineEnd = source.find('\n', lineStart);
|
||||||
|
const bool hasLineBreak = lineEnd != MobileGL::String::npos;
|
||||||
|
if (!hasLineBreak) {
|
||||||
|
lineEnd = source.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MobileGL::String line = source.substr(lineStart, lineEnd - lineStart);
|
||||||
|
SizeT probe = 0;
|
||||||
|
while (probe < line.size() && std::isspace(static_cast<unsigned char>(line[probe]))) {
|
||||||
|
probe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe < line.size() && line[probe] == '#') {
|
||||||
|
probe++;
|
||||||
|
while (probe < line.size() && std::isspace(static_cast<unsigned char>(line[probe]))) {
|
||||||
|
probe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* extensionToken = "extension";
|
||||||
|
constexpr SizeT extensionLen = 9;
|
||||||
|
const bool hasExtensionDirective =
|
||||||
|
probe + extensionLen <= line.size() &&
|
||||||
|
line.compare(probe, extensionLen, extensionToken) == 0 &&
|
||||||
|
(probe + extensionLen == line.size() || !IsIdentifierChar(line[probe + extensionLen]));
|
||||||
|
if (hasExtensionDirective) {
|
||||||
|
probe += extensionLen;
|
||||||
|
while (probe < line.size() && std::isspace(static_cast<unsigned char>(line[probe]))) {
|
||||||
|
probe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* int64Extension = "GL_ARB_gpu_shader_int64";
|
||||||
|
constexpr SizeT int64ExtensionLen = 23;
|
||||||
|
const bool hasInt64Extension =
|
||||||
|
probe + int64ExtensionLen <= line.size() &&
|
||||||
|
line.compare(probe, int64ExtensionLen, int64Extension) == 0 &&
|
||||||
|
(probe + int64ExtensionLen == line.size() ||
|
||||||
|
!IsIdentifierChar(line[probe + int64ExtensionLen]));
|
||||||
|
if (hasInt64Extension) {
|
||||||
|
probe += int64ExtensionLen;
|
||||||
|
while (probe < line.size() && std::isspace(static_cast<unsigned char>(line[probe]))) {
|
||||||
|
probe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe < line.size() && line[probe] == ':') {
|
||||||
|
probe++;
|
||||||
|
const MobileGL::String behavior = TrimDirectiveToken(line.substr(probe));
|
||||||
|
const SizeT replaceLen = lineEnd - lineStart + (hasLineBreak ? 1 : 0);
|
||||||
|
if (behavior == "require") {
|
||||||
|
const MobileGL::String replacement =
|
||||||
|
"#error GL_ARB_gpu_shader_int64 is not advertised by MobileGL\n";
|
||||||
|
source.replace(lineStart, replaceLen, replacement);
|
||||||
|
lineStart += replacement.size();
|
||||||
|
} else if (behavior == "enable" || behavior == "warn") {
|
||||||
|
source.replace(lineStart, replaceLen, "\n");
|
||||||
|
lineStart++;
|
||||||
|
} else {
|
||||||
|
lineStart = lineEnd + (hasLineBreak ? 1 : 0);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lineStart = lineEnd + (hasLineBreak ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceIdentifier(source, "GL_ARB_gpu_shader_int64", "MG_DISABLED_GL_ARB_gpu_shader_int64");
|
||||||
|
}
|
||||||
|
|
||||||
void ModernizeLegacyGLSL(MobileGL::ShaderStage stage, MobileGL::String& source) {
|
void ModernizeLegacyGLSL(MobileGL::ShaderStage stage, MobileGL::String& source) {
|
||||||
RemoveDefineForIdentifier(source, "HIGHP_OR_DEFAULT");
|
RemoveDefineForIdentifier(source, "HIGHP_OR_DEFAULT");
|
||||||
RemoveDefineForIdentifier(source, "MEDIUMP_OR_DEFAULT");
|
RemoveDefineForIdentifier(source, "MEDIUMP_OR_DEFAULT");
|
||||||
@@ -278,6 +380,8 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilterUnsupportedGpuShaderInt64(source);
|
||||||
|
|
||||||
// Some shader packs define helpers with built-in GLSL names such as round(), tanh(), or fma().
|
// Some shader packs define helpers with built-in GLSL names such as round(), tanh(), or fma().
|
||||||
// These may pass OpenGL-style validation but fail when recompiled for Vulkan/SPIR-V generation.
|
// These may pass OpenGL-style validation but fail when recompiled for Vulkan/SPIR-V generation.
|
||||||
RenameBuiltinShadowingFunction(source, "round", "mg_round");
|
RenameBuiltinShadowingFunction(source, "round", "mg_round");
|
||||||
|
|||||||
Reference in New Issue
Block a user