mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18: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 {
|
||||
SizeT UniformBufferOffsetAlignment = 256;
|
||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||
Uint32 SubgroupSize = 0;
|
||||
Uint32 SubgroupSupportedStages = 0;
|
||||
Uint32 SubgroupSupportedFeatures = 0;
|
||||
Bool SubgroupQuadOperationsInAllStages = false;
|
||||
};
|
||||
|
||||
enum class WindowBackend {
|
||||
|
||||
@@ -19,6 +19,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
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() {
|
||||
if (!m_windowHandle.Handle) {
|
||||
MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null");
|
||||
@@ -47,8 +68,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
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();
|
||||
UpdateAdvertisedExtensions();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,27 +140,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
|
||||
static RendererInfo 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;
|
||||
return m_rendererInfo;
|
||||
}
|
||||
|
||||
String BackendObject_DirectVulkan::GetBackendAPIVersionString() const {
|
||||
@@ -214,11 +221,81 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
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() {
|
||||
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;
|
||||
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
|
||||
m_dynamicParameters.MaxShaderStorageBlockSize =
|
||||
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) {
|
||||
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
|
||||
m_vulkanCaps.MaxShaderStorageBlockSize,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class BackendObject_DirectVulkan : public BackendObject {
|
||||
public:
|
||||
BackendObject_DirectVulkan();
|
||||
~BackendObject_DirectVulkan() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -30,12 +31,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
|
||||
const DynamicBackendParameters& GetDynamicParameters() const override;
|
||||
BackendType GetBackendType() const override;
|
||||
void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities);
|
||||
|
||||
private:
|
||||
void UpdateAdvertisedExtensions();
|
||||
void UpdateDynamicBackendParameters();
|
||||
|
||||
Bool m_initialized = false;
|
||||
DynamicBackendParameters m_dynamicParameters;
|
||||
MG_External::VulkanCapabilities m_vulkanCaps;
|
||||
RendererInfo m_rendererInfo;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -2920,16 +2920,7 @@ void main() {
|
||||
vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast<VkDeviceSize>(indirect));
|
||||
}
|
||||
|
||||
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 VulkanRenderer::BuildMemoryBarrierForGlBarriers(GLbitfield barriers) {
|
||||
VkMemoryBarrier memoryBarrier{};
|
||||
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||
memoryBarrier.srcAccessMask =
|
||||
@@ -2945,6 +2936,24 @@ void main() {
|
||||
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;
|
||||
|
||||
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));
|
||||
vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
|
||||
@@ -5331,6 +5340,10 @@ void main() {
|
||||
return m_physicalDevice;
|
||||
}
|
||||
|
||||
VkInstance VulkanRenderer::GetInstance() const {
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::IsDrawIndirectCountExtensionEnabled() const {
|
||||
return m_drawIndirectCountExtensionEnabled;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
|
||||
void DispatchComputeIndirect(GLintptr indirect);
|
||||
void MemoryBarrier(GLbitfield barriers);
|
||||
static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers);
|
||||
void DrawArrays(const DrawCmd& payload);
|
||||
void DrawElements(const DrawIndexedCmd& payload);
|
||||
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
||||
@@ -151,6 +152,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void Present();
|
||||
|
||||
const PhysicalDevice& GetPhysicalDevice() const;
|
||||
VkInstance GetInstance() const;
|
||||
Bool IsDrawIndirectCountExtensionEnabled() const;
|
||||
|
||||
void RecreateSwapchain();
|
||||
|
||||
@@ -76,12 +76,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str());
|
||||
return (const GLubyte*)shadingLanguageVersion.c_str();
|
||||
case GL_EXTENSIONS:
|
||||
if (extensionsString.empty()) {
|
||||
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
|
||||
extensionsString += MG_Util::ConvertGLExtToString(ext);
|
||||
extensionsString.clear();
|
||||
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
|
||||
if (!extensionsString.empty()) {
|
||||
extensionsString += " ";
|
||||
}
|
||||
extensionsString.pop_back();
|
||||
extensionsString += MG_Util::ConvertGLExtToString(ext);
|
||||
}
|
||||
return (const GLubyte*)extensionsString.c_str();
|
||||
default:
|
||||
@@ -108,13 +108,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
|
||||
static Vector<String> extStrings;
|
||||
static Bool initialized = false;
|
||||
if (!initialized) {
|
||||
extStrings.reserve(exts.size());
|
||||
for (const auto& ext : exts) {
|
||||
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
|
||||
}
|
||||
initialized = true;
|
||||
extStrings.clear();
|
||||
extStrings.reserve(exts.size());
|
||||
for (const auto& ext : exts) {
|
||||
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
|
||||
}
|
||||
|
||||
return (const GLubyte*)extStrings[index].c_str();
|
||||
@@ -174,6 +171,15 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
*data = static_cast<GLint64>(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize);
|
||||
}
|
||||
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:
|
||||
*data = 0;
|
||||
MG_State::pGLContext->RecordError(
|
||||
@@ -200,6 +206,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
|
||||
const auto& dynamicParameters = activeBackendObject->GetDynamicParameters();
|
||||
|
||||
switch (pname) {
|
||||
case GL_ACTIVE_TEXTURE:
|
||||
@@ -277,6 +284,18 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_COMPRESSED_TEXTURE_FORMATS:
|
||||
*params = 0; // TODO
|
||||
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:
|
||||
*params = 16; // TODO: use backend value
|
||||
break;
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
||||
|
||||
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;
|
||||
|
||||
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "Includes.h"
|
||||
#include "Init.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/Program/GL_Program.h"
|
||||
#include "MG_State/GLState/Core.h"
|
||||
@@ -118,6 +120,80 @@ TEST_F(ProgramTest, CompileFragment) {
|
||||
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) {
|
||||
const char* part0 = R"(#define HIGHP_OR_DEFAULT highp
|
||||
attribute vec4 Position;
|
||||
|
||||
@@ -21,9 +21,44 @@
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.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>
|
||||
|
||||
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) {
|
||||
#if defined(_WIN32)
|
||||
_putenv_s(name, value);
|
||||
@@ -87,6 +122,137 @@ TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaising
|
||||
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) {
|
||||
using namespace MobileGL;
|
||||
using namespace MobileGL::MG_Backend::DirectVulkan;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Includes.h"
|
||||
#include "Init.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_State/GLState/Core.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);
|
||||
}
|
||||
|
||||
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) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include "Loader.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace MobileGL::MG_Util::BackendLoader {
|
||||
namespace {
|
||||
struct VulkanDynamicFunctions {
|
||||
@@ -33,6 +36,14 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
|
||||
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
|
||||
|
||||
inline Version DecodeApiVersion(uint32_t version) {
|
||||
@@ -46,6 +57,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
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,
|
||||
VkPhysicalDevice physicalDevice) {
|
||||
if (!physicalDevice) {
|
||||
@@ -69,8 +86,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceSubgroupProperties subgroupProps{};
|
||||
subgroupProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
|
||||
|
||||
VkPhysicalDeviceProperties2 props2{};
|
||||
props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
props2.pNext = &subgroupProps;
|
||||
if (vk.vkGetPhysicalDeviceProperties2) {
|
||||
vk.vkGetPhysicalDeviceProperties2(physicalDevice, &props2);
|
||||
} else {
|
||||
@@ -84,6 +105,28 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
caps.DriverVersionString = DecodeDriverVersion(p.driverVersion);
|
||||
caps.UniformBufferOffsetAlignment = static_cast<int>(p.limits.minUniformBufferOffsetAlignment);
|
||||
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;
|
||||
}
|
||||
@@ -95,5 +138,10 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion);
|
||||
caps.UniformBufferOffsetAlignment = static_cast<int>(properties.limits.minUniformBufferOffsetAlignment);
|
||||
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
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace MobileGL {
|
||||
String DriverVersionString;
|
||||
Int UniformBufferOffsetAlignment = 256;
|
||||
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_Util::BackendLoader {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
#include "ShaderSourceProcessor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
|
||||
namespace {
|
||||
using MobileGL::SizeT;
|
||||
@@ -159,6 +161,106 @@ namespace {
|
||||
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) {
|
||||
RemoveDefineForIdentifier(source, "HIGHP_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().
|
||||
// These may pass OpenGL-style validation but fail when recompiled for Vulkan/SPIR-V generation.
|
||||
RenameBuiltinShadowingFunction(source, "round", "mg_round");
|
||||
|
||||
Reference in New Issue
Block a user