mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Feat] (MG_Backend, MG_Impl): expose experimental GL 4.6 CTS limits
This commit is contained in:
@@ -300,6 +300,13 @@ namespace MobileGL {
|
|||||||
Float ViewportBoundsRangeMin = 0.0f;
|
Float ViewportBoundsRangeMin = 0.0f;
|
||||||
Float ViewportBoundsRangeMax = 0.0f;
|
Float ViewportBoundsRangeMax = 0.0f;
|
||||||
Int ViewportSubpixelBits = 0;
|
Int ViewportSubpixelBits = 0;
|
||||||
|
// GL 4.x fragment-interpolation offset limits. These defaults are the
|
||||||
|
// core minimums and are replaced by live GLES/Vulkan device limits.
|
||||||
|
Float MinFragmentInterpolationOffset = -0.5f;
|
||||||
|
// For four fractional bits the greatest required legal offset is
|
||||||
|
// 0.5 - 2^-4 = 0.4375 (GL 4.6 table 23.70).
|
||||||
|
Float MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
Int FragmentInterpolationOffsetBits = 4;
|
||||||
Bool SupportsWideLines = false;
|
Bool SupportsWideLines = false;
|
||||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||||
Uint32 SubgroupSize = 0;
|
Uint32 SubgroupSize = 0;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <MG_Util/Texture/TextureFormatProcessor.h>
|
#include <MG_Util/Texture/TextureFormatProcessor.h>
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectGLES {
|
namespace MobileGL::MG_Backend::DirectGLES {
|
||||||
@@ -603,7 +604,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
.ExtraVendor = Nullopt, // Extra vendor
|
.ExtraVendor = Nullopt, // Extra vendor
|
||||||
.RendererGLInfo =
|
.RendererGLInfo =
|
||||||
{
|
{
|
||||||
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
|
.TargetGLVersion = {4, 6, 0}, // Experimental GL CTS target version
|
||||||
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
||||||
// Baseline advertisement (no timer queries / anisotropy yet); reconciled
|
// Baseline advertisement (no timer queries / anisotropy yet); reconciled
|
||||||
// once the ES capabilities exist, see UpdateAdvertisedCapabilityExtensions.
|
// once the ES capabilities exist, see UpdateAdvertisedCapabilityExtensions.
|
||||||
@@ -1034,6 +1035,24 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
m_dynamicParameters.ViewportBoundsRangeMin = m_GLESCapabilities.ViewportBoundsRangeMin;
|
m_dynamicParameters.ViewportBoundsRangeMin = m_GLESCapabilities.ViewportBoundsRangeMin;
|
||||||
m_dynamicParameters.ViewportBoundsRangeMax = m_GLESCapabilities.ViewportBoundsRangeMax;
|
m_dynamicParameters.ViewportBoundsRangeMax = m_GLESCapabilities.ViewportBoundsRangeMax;
|
||||||
m_dynamicParameters.ViewportSubpixelBits = m_GLESCapabilities.ViewportSubpixelBits;
|
m_dynamicParameters.ViewportSubpixelBits = m_GLESCapabilities.ViewportSubpixelBits;
|
||||||
|
m_dynamicParameters.MinFragmentInterpolationOffset =
|
||||||
|
std::isfinite(m_GLESCapabilities.MinFragmentInterpolationOffset) &&
|
||||||
|
m_GLESCapabilities.MinFragmentInterpolationOffset <= -0.5f
|
||||||
|
? m_GLESCapabilities.MinFragmentInterpolationOffset
|
||||||
|
: -0.5f;
|
||||||
|
m_dynamicParameters.MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
||||||
|
if (m_GLESCapabilities.FragmentInterpolationOffsetBits >= 4 &&
|
||||||
|
std::isfinite(m_GLESCapabilities.MaxFragmentInterpolationOffset)) {
|
||||||
|
const Float requiredMaxOffset =
|
||||||
|
0.5f - std::ldexp(1.0f, -m_GLESCapabilities.FragmentInterpolationOffsetBits);
|
||||||
|
if (m_GLESCapabilities.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
|
m_dynamicParameters.MaxFragmentInterpolationOffset =
|
||||||
|
m_GLESCapabilities.MaxFragmentInterpolationOffset;
|
||||||
|
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
||||||
|
m_GLESCapabilities.FragmentInterpolationOffsetBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
m_dynamicParameters.SupportsWideLines =
|
m_dynamicParameters.SupportsWideLines =
|
||||||
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "MG_Util/Texture/TextureFormatProcessor.h"
|
#include "MG_Util/Texture/TextureFormatProcessor.h"
|
||||||
|
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -505,7 +506,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.ExtraVendor = Nullopt,
|
.ExtraVendor = Nullopt,
|
||||||
.RendererGLInfo =
|
.RendererGLInfo =
|
||||||
{
|
{
|
||||||
.TargetGLVersion = {3, 3, 0},
|
.TargetGLVersion = {4, 6, 0}, // Experimental GL CTS target version
|
||||||
.TargetGLSLVersion = {4, 6, 0},
|
.TargetGLSLVersion = {4, 6, 0},
|
||||||
// Baseline advertisement (no shader subgroup, no timer queries); a
|
// Baseline advertisement (no shader subgroup, no timer queries); a
|
||||||
// live backend reconciles its copy in UpdateAdvertisedExtensions.
|
// live backend reconciles its copy in UpdateAdvertisedExtensions.
|
||||||
@@ -796,6 +797,23 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.ViewportBoundsRangeMin = m_vulkanCaps.ViewportBoundsRangeMin;
|
m_dynamicParameters.ViewportBoundsRangeMin = m_vulkanCaps.ViewportBoundsRangeMin;
|
||||||
m_dynamicParameters.ViewportBoundsRangeMax = m_vulkanCaps.ViewportBoundsRangeMax;
|
m_dynamicParameters.ViewportBoundsRangeMax = m_vulkanCaps.ViewportBoundsRangeMax;
|
||||||
m_dynamicParameters.ViewportSubpixelBits = m_vulkanCaps.ViewportSubpixelBits;
|
m_dynamicParameters.ViewportSubpixelBits = m_vulkanCaps.ViewportSubpixelBits;
|
||||||
|
m_dynamicParameters.MinFragmentInterpolationOffset =
|
||||||
|
std::isfinite(m_vulkanCaps.MinFragmentInterpolationOffset) &&
|
||||||
|
m_vulkanCaps.MinFragmentInterpolationOffset <= -0.5f
|
||||||
|
? m_vulkanCaps.MinFragmentInterpolationOffset
|
||||||
|
: -0.5f;
|
||||||
|
m_dynamicParameters.MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
||||||
|
if (m_vulkanCaps.FragmentInterpolationOffsetBits >= 4 &&
|
||||||
|
std::isfinite(m_vulkanCaps.MaxFragmentInterpolationOffset)) {
|
||||||
|
const Float requiredMaxOffset =
|
||||||
|
0.5f - std::ldexp(1.0f, -m_vulkanCaps.FragmentInterpolationOffsetBits);
|
||||||
|
if (m_vulkanCaps.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
|
m_dynamicParameters.MaxFragmentInterpolationOffset = m_vulkanCaps.MaxFragmentInterpolationOffset;
|
||||||
|
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
||||||
|
m_vulkanCaps.FragmentInterpolationOffsetBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
||||||
m_dynamicParameters.MaxShaderStorageBlockSize =
|
m_dynamicParameters.MaxShaderStorageBlockSize =
|
||||||
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
|
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
|
||||||
|
|||||||
@@ -465,6 +465,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_STENCIL_TEST:
|
case GL_STENCIL_TEST:
|
||||||
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest) ? GL_TRUE : GL_FALSE;
|
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest) ? GL_TRUE : GL_FALSE;
|
||||||
return;
|
return;
|
||||||
|
case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS: {
|
||||||
|
GLfloat value = 0.0f;
|
||||||
|
GetFloatv(pname, &value);
|
||||||
|
*params = value != 0.0f ? GL_TRUE : GL_FALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -520,6 +528,19 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
params[1] = dynamicParameters.ViewportBoundsRangeMax;
|
params[1] = dynamicParameters.ViewportBoundsRangeMax;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS: {
|
||||||
|
const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
||||||
|
if (pname == GL_MIN_FRAGMENT_INTERPOLATION_OFFSET) {
|
||||||
|
params[0] = dynamicParameters.MinFragmentInterpolationOffset;
|
||||||
|
} else if (pname == GL_MAX_FRAGMENT_INTERPOLATION_OFFSET) {
|
||||||
|
params[0] = dynamicParameters.MaxFragmentInterpolationOffset;
|
||||||
|
} else {
|
||||||
|
params[0] = static_cast<GLfloat>(dynamicParameters.FragmentInterpolationOffsetBits);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
case GL_DEPTH_CLEAR_VALUE:
|
case GL_DEPTH_CLEAR_VALUE:
|
||||||
params[0] = MG_State::pGLContext->GetClearDepth();
|
params[0] = MG_State::pGLContext->GetClearDepth();
|
||||||
return;
|
return;
|
||||||
@@ -1957,6 +1978,15 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_SUBPIXEL_BITS:
|
case GL_SUBPIXEL_BITS:
|
||||||
*params = std::max(dynamicParameters.ViewportSubpixelBits, kFrontendSubpixelBits);
|
*params = std::max(dynamicParameters.ViewportSubpixelBits, kFrontendSubpixelBits);
|
||||||
break;
|
break;
|
||||||
|
case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
*params = static_cast<GLint>(std::lround(dynamicParameters.MinFragmentInterpolationOffset));
|
||||||
|
break;
|
||||||
|
case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
*params = static_cast<GLint>(std::lround(dynamicParameters.MaxFragmentInterpolationOffset));
|
||||||
|
break;
|
||||||
|
case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS:
|
||||||
|
*params = dynamicParameters.FragmentInterpolationOffsetBits;
|
||||||
|
break;
|
||||||
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
|
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
|
||||||
*params = static_cast<Int>(dynamicParameters.UniformBufferOffsetAlignment);
|
*params = static_cast<Int>(dynamicParameters.UniformBufferOffsetAlignment);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ namespace {
|
|||||||
GLint maxFragmentImageUniforms = 4;
|
GLint maxFragmentImageUniforms = 4;
|
||||||
GLint maxComputeImageUniforms = 5;
|
GLint maxComputeImageUniforms = 5;
|
||||||
bool maxGeometryImageUniformsQueried = false;
|
bool maxGeometryImageUniformsQueried = false;
|
||||||
|
GLfloat minFragmentInterpolationOffset = -0.75f;
|
||||||
|
GLfloat maxFragmentInterpolationOffset = 0.625f;
|
||||||
|
GLint fragmentInterpolationOffsetBits = 6;
|
||||||
|
bool fragmentInterpolationLimitsQueried = false;
|
||||||
|
bool fragmentInterpolationQueryRaisesError = false;
|
||||||
// Emulates ANGLE-on-Vulkan: the draw reads the indirect command's
|
// Emulates ANGLE-on-Vulkan: the draw reads the indirect command's
|
||||||
// baseInstance word and exposes it through gl_InstanceID.
|
// baseInstance word and exposes it through gl_InstanceID.
|
||||||
bool drawLeaksBaseInstanceWord = false;
|
bool drawLeaksBaseInstanceWord = false;
|
||||||
@@ -108,6 +113,14 @@ namespace {
|
|||||||
case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
|
case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
|
||||||
*data = g_fake.maxComputeImageUniforms;
|
*data = g_fake.maxComputeImageUniforms;
|
||||||
break;
|
break;
|
||||||
|
case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS:
|
||||||
|
g_fake.fragmentInterpolationLimitsQueried = true;
|
||||||
|
if (g_fake.fragmentInterpolationQueryRaisesError) {
|
||||||
|
g_fake.pendingError = GL_INVALID_ENUM;
|
||||||
|
} else {
|
||||||
|
*data = g_fake.fragmentInterpolationOffsetBits;
|
||||||
|
}
|
||||||
|
break;
|
||||||
// FillInGLESCapabilities reads the context version before running the
|
// FillInGLESCapabilities reads the context version before running the
|
||||||
// baseInstance probe, which requires ES >= 3.1.
|
// baseInstance probe, which requires ES >= 3.1.
|
||||||
case GL_MAJOR_VERSION:
|
case GL_MAJOR_VERSION:
|
||||||
@@ -155,6 +168,22 @@ namespace {
|
|||||||
g_fake.maxTextureMaxAnisotropyQueried = true;
|
g_fake.maxTextureMaxAnisotropyQueried = true;
|
||||||
data[0] = g_fake.maxTextureMaxAnisotropy;
|
data[0] = g_fake.maxTextureMaxAnisotropy;
|
||||||
break;
|
break;
|
||||||
|
case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
g_fake.fragmentInterpolationLimitsQueried = true;
|
||||||
|
if (g_fake.fragmentInterpolationQueryRaisesError) {
|
||||||
|
g_fake.pendingError = GL_INVALID_ENUM;
|
||||||
|
} else {
|
||||||
|
data[0] = g_fake.minFragmentInterpolationOffset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET:
|
||||||
|
g_fake.fragmentInterpolationLimitsQueried = true;
|
||||||
|
if (g_fake.fragmentInterpolationQueryRaisesError) {
|
||||||
|
g_fake.pendingError = GL_INVALID_ENUM;
|
||||||
|
} else {
|
||||||
|
data[0] = g_fake.maxFragmentInterpolationOffset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
// Two-component range queries.
|
// Two-component range queries.
|
||||||
case GL_ALIASED_LINE_WIDTH_RANGE:
|
case GL_ALIASED_LINE_WIDTH_RANGE:
|
||||||
case GL_SMOOTH_LINE_WIDTH_RANGE:
|
case GL_SMOOTH_LINE_WIDTH_RANGE:
|
||||||
@@ -462,6 +491,52 @@ TEST(ImageUniformCapabilities, QueriesRealPerStageLimitsAndConservativelyGatesGe
|
|||||||
EXPECT_TRUE(g_fake.maxGeometryImageUniformsQueried);
|
EXPECT_TRUE(g_fake.maxGeometryImageUniformsQueried);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FragmentInterpolationCapabilities, QueriesOnlyWhenSupportedAndPreservesDriverLimits) {
|
||||||
|
const auto funcs = MakeFakeGLESFunctions();
|
||||||
|
|
||||||
|
ResetFakeDriver();
|
||||||
|
g_fake.maxVertexSsboBlocks = 0;
|
||||||
|
MobileGL::MG_External::GLESCapabilities unsupportedCaps;
|
||||||
|
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(unsupportedCaps, funcs));
|
||||||
|
EXPECT_FALSE(unsupportedCaps.SupportsShaderMultisampleInterpolation);
|
||||||
|
EXPECT_FALSE(g_fake.fragmentInterpolationLimitsQueried);
|
||||||
|
EXPECT_FLOAT_EQ(unsupportedCaps.MinFragmentInterpolationOffset, -0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(unsupportedCaps.MaxFragmentInterpolationOffset, 0.4375f);
|
||||||
|
EXPECT_EQ(unsupportedCaps.FragmentInterpolationOffsetBits, 4);
|
||||||
|
|
||||||
|
ResetFakeDriver();
|
||||||
|
g_fake.maxVertexSsboBlocks = 0;
|
||||||
|
g_fake.extensions.emplace_back("GL_OES_shader_multisample_interpolation");
|
||||||
|
// A stale error from an earlier capability probe must not make the optional
|
||||||
|
// interpolation query look like it failed.
|
||||||
|
g_fake.pendingError = GL_INVALID_OPERATION;
|
||||||
|
MobileGL::MG_External::GLESCapabilities supportedCaps;
|
||||||
|
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(supportedCaps, funcs));
|
||||||
|
EXPECT_TRUE(supportedCaps.SupportsShaderMultisampleInterpolation);
|
||||||
|
EXPECT_TRUE(g_fake.fragmentInterpolationLimitsQueried);
|
||||||
|
EXPECT_FLOAT_EQ(supportedCaps.MinFragmentInterpolationOffset, g_fake.minFragmentInterpolationOffset);
|
||||||
|
EXPECT_FLOAT_EQ(supportedCaps.MaxFragmentInterpolationOffset, g_fake.maxFragmentInterpolationOffset);
|
||||||
|
EXPECT_EQ(supportedCaps.FragmentInterpolationOffsetBits, g_fake.fragmentInterpolationOffsetBits);
|
||||||
|
EXPECT_EQ(funcs.glGetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FragmentInterpolationCapabilities, QueryErrorIsDrainedAndFallsBackToCoreMinimums) {
|
||||||
|
ResetFakeDriver();
|
||||||
|
g_fake.maxVertexSsboBlocks = 0;
|
||||||
|
g_fake.extensions.emplace_back("GL_OES_shader_multisample_interpolation");
|
||||||
|
g_fake.fragmentInterpolationQueryRaisesError = true;
|
||||||
|
const auto funcs = MakeFakeGLESFunctions();
|
||||||
|
|
||||||
|
MobileGL::MG_External::GLESCapabilities caps;
|
||||||
|
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(caps, funcs));
|
||||||
|
|
||||||
|
EXPECT_TRUE(g_fake.fragmentInterpolationLimitsQueried);
|
||||||
|
EXPECT_FLOAT_EQ(caps.MinFragmentInterpolationOffset, -0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(caps.MaxFragmentInterpolationOffset, 0.4375f);
|
||||||
|
EXPECT_EQ(caps.FragmentInterpolationOffsetBits, 4);
|
||||||
|
EXPECT_EQ(funcs.glGetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
// The extension string is what apps gate on (LWJGL builds GLCapabilities from it), so advertising
|
// The extension string is what apps gate on (LWJGL builds GLCapabilities from it), so advertising
|
||||||
// it on a driver that cannot filter anisotropically would leave them silently on trilinear.
|
// it on a driver that cannot filter anisotropically would leave them silently on trilinear.
|
||||||
TEST(TextureAnisotropyCapabilities, ExtensionIsAdvertisedOnlyWhenTheHostDriverSupportsIt) {
|
TEST(TextureAnisotropyCapabilities, ExtensionIsAdvertisedOnlyWhenTheHostDriverSupportsIt) {
|
||||||
|
|||||||
@@ -197,13 +197,13 @@ TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) {
|
|||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) {
|
TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensionsAtExperimentalCTSVersion) {
|
||||||
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
||||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||||
const auto& extensions = rendererInfo.Extensions;
|
const auto& extensions = rendererInfo.Extensions;
|
||||||
|
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 4);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 6);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
||||||
|
|
||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
||||||
@@ -397,13 +397,13 @@ TEST(DirectVulkanSanity, RenderPassExtentUsesSwapchainSizeOnlyForDefaultFramebuf
|
|||||||
MobileGL::IntVec2(512, 512));
|
MobileGL::IntVec2(512, 512));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) {
|
TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsAtExperimentalCTSVersion) {
|
||||||
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||||
const auto& extensions = rendererInfo.Extensions;
|
const auto& extensions = rendererInfo.Extensions;
|
||||||
|
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 4);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 6);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
||||||
|
|
||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
||||||
@@ -516,6 +516,50 @@ TEST(DirectGLESSanity, PreservesHostPerStageImageUniformLimits) {
|
|||||||
EXPECT_EQ(params.MaxComputeImageUniforms, 5);
|
EXPECT_EQ(params.MaxComputeImageUniforms, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FragmentInterpolationCapabilities, PlumbsGLESAndBothVulkanPropertyPaths) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
MG_External::GLESCapabilities glesCaps;
|
||||||
|
glesCaps.MinFragmentInterpolationOffset = -0.75f;
|
||||||
|
glesCaps.MaxFragmentInterpolationOffset = 0.625f;
|
||||||
|
glesCaps.FragmentInterpolationOffsetBits = 6;
|
||||||
|
MG_Backend::DirectGLES::BackendObject_DirectGLES glesBackend;
|
||||||
|
glesBackend.ApplyGLESCapabilitiesForTesting(glesCaps);
|
||||||
|
EXPECT_FLOAT_EQ(glesBackend.GetDynamicParameters().MinFragmentInterpolationOffset, -0.75f);
|
||||||
|
EXPECT_FLOAT_EQ(glesBackend.GetDynamicParameters().MaxFragmentInterpolationOffset, 0.625f);
|
||||||
|
EXPECT_EQ(glesBackend.GetDynamicParameters().FragmentInterpolationOffsetBits, 6);
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties properties{};
|
||||||
|
// A common Vulkan limit pair: max is one representable 4-bit step below 0.5.
|
||||||
|
properties.limits.minInterpolationOffset = -0.5f;
|
||||||
|
properties.limits.maxInterpolationOffset = 0.4375f;
|
||||||
|
properties.limits.subPixelInterpolationOffsetBits = 4;
|
||||||
|
MG_External::VulkanCapabilities vkCaps;
|
||||||
|
MG_Util::BackendLoader::FillInVulkanCapabilities(vkCaps, properties);
|
||||||
|
EXPECT_FLOAT_EQ(vkCaps.MinFragmentInterpolationOffset, -0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(vkCaps.MaxFragmentInterpolationOffset, 0.4375f);
|
||||||
|
EXPECT_EQ(vkCaps.FragmentInterpolationOffsetBits, 4);
|
||||||
|
|
||||||
|
vkCaps.MinFragmentInterpolationOffset = -0.875f;
|
||||||
|
vkCaps.MaxFragmentInterpolationOffset = 0.75f;
|
||||||
|
vkCaps.FragmentInterpolationOffsetBits = 7;
|
||||||
|
MG_Backend::DirectVulkan::BackendObject_DirectVulkan vkBackend;
|
||||||
|
vkBackend.ApplyVulkanCapabilitiesForTesting(vkCaps);
|
||||||
|
EXPECT_FLOAT_EQ(vkBackend.GetDynamicParameters().MinFragmentInterpolationOffset, -0.875f);
|
||||||
|
EXPECT_FLOAT_EQ(vkBackend.GetDynamicParameters().MaxFragmentInterpolationOffset, 0.75f);
|
||||||
|
EXPECT_EQ(vkBackend.GetDynamicParameters().FragmentInterpolationOffsetBits, 7);
|
||||||
|
|
||||||
|
// Invalid/zero host data cannot under-advertise the OpenGL 4 minimums.
|
||||||
|
MG_External::VulkanCapabilities invalidCaps;
|
||||||
|
invalidCaps.MinFragmentInterpolationOffset = 0.0f;
|
||||||
|
invalidCaps.MaxFragmentInterpolationOffset = 0.0f;
|
||||||
|
invalidCaps.FragmentInterpolationOffsetBits = 0;
|
||||||
|
vkBackend.ApplyVulkanCapabilitiesForTesting(invalidCaps);
|
||||||
|
EXPECT_LE(vkBackend.GetDynamicParameters().MinFragmentInterpolationOffset, -0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(vkBackend.GetDynamicParameters().MaxFragmentInterpolationOffset, 0.4375f);
|
||||||
|
EXPECT_EQ(vkBackend.GetDynamicParameters().FragmentInterpolationOffsetBits, 4);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DirectVulkanSanity, AdvertisesSubgroupOnlyWhenVulkanReportsUsableSupport) {
|
TEST(DirectVulkanSanity, AdvertisesSubgroupOnlyWhenVulkanReportsUsableSupport) {
|
||||||
using namespace MobileGL;
|
using namespace MobileGL;
|
||||||
|
|
||||||
@@ -638,6 +682,48 @@ TEST(GetterSanity, ClampsMaxVertexAttribsToCurrentValueStorageCapacity) {
|
|||||||
MG_State::pGLContext.reset();
|
MG_State::pGLContext.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GetterSanity, ReportsFragmentInterpolationLimitsForFloatAndIntegerQueries) {
|
||||||
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
auto previousContext = Move(MG_State::pGLContext);
|
||||||
|
auto previousBackend = Move(MG_Backend::pActiveBackendObject);
|
||||||
|
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
|
||||||
|
|
||||||
|
MG_Backend::DynamicBackendParameters params;
|
||||||
|
params.MinFragmentInterpolationOffset = -0.75f;
|
||||||
|
params.MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
params.FragmentInterpolationOffsetBits = 6;
|
||||||
|
MG_Backend::pActiveBackendObject = MakeUnique<DynamicParameterBackend>(params);
|
||||||
|
|
||||||
|
GLfloat floatValue = 0.0f;
|
||||||
|
MG_Impl::GLImpl::GetFloatv(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, &floatValue);
|
||||||
|
EXPECT_FLOAT_EQ(floatValue, -0.75f);
|
||||||
|
MG_Impl::GLImpl::GetFloatv(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, &floatValue);
|
||||||
|
EXPECT_FLOAT_EQ(floatValue, 0.4375f);
|
||||||
|
MG_Impl::GLImpl::GetFloatv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &floatValue);
|
||||||
|
EXPECT_FLOAT_EQ(floatValue, 6.0f);
|
||||||
|
|
||||||
|
GLint intValue = 0;
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, &intValue);
|
||||||
|
EXPECT_EQ(intValue, -1);
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, &intValue);
|
||||||
|
EXPECT_EQ(intValue, 0);
|
||||||
|
MG_Impl::GLImpl::GetIntegerv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &intValue);
|
||||||
|
EXPECT_EQ(intValue, 6);
|
||||||
|
|
||||||
|
GLboolean boolValue = GL_FALSE;
|
||||||
|
MG_Impl::GLImpl::GetBooleanv(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, &boolValue);
|
||||||
|
EXPECT_EQ(boolValue, GL_TRUE);
|
||||||
|
MG_Impl::GLImpl::GetBooleanv(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, &boolValue);
|
||||||
|
EXPECT_EQ(boolValue, GL_TRUE);
|
||||||
|
MG_Impl::GLImpl::GetBooleanv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &boolValue);
|
||||||
|
EXPECT_EQ(boolValue, GL_TRUE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MG_Backend::pActiveBackendObject = Move(previousBackend);
|
||||||
|
MG_State::pGLContext = Move(previousContext);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(GetterSanity, PerStageImageUniformQueriesMatchShaderCompilerLimits) {
|
TEST(GetterSanity, PerStageImageUniformQueriesMatchShaderCompilerLimits) {
|
||||||
using namespace MobileGL;
|
using namespace MobileGL;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "Loader.h"
|
#include "Loader.h"
|
||||||
#include "MG_Util/Types.h"
|
#include "MG_Util/Types.h"
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
|
#include <cmath>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN 1
|
#define WIN32_LEAN_AND_MEAN 1
|
||||||
@@ -838,8 +839,14 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
if (std::strcmp(extension, "GL_NV_shader_noperspective_interpolation") == 0) {
|
if (std::strcmp(extension, "GL_NV_shader_noperspective_interpolation") == 0) {
|
||||||
caps.SupportsNoperspectiveInterpolation = true;
|
caps.SupportsNoperspectiveInterpolation = true;
|
||||||
}
|
}
|
||||||
|
if (std::strcmp(extension, "GL_OES_shader_multisample_interpolation") == 0) {
|
||||||
|
caps.SupportsShaderMultisampleInterpolation = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
caps.SupportsShaderMultisampleInterpolation =
|
||||||
|
caps.SupportsShaderMultisampleInterpolation || caps.GLESVersion.Major > 3 ||
|
||||||
|
(caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2);
|
||||||
|
|
||||||
// Detect optional raster/color-mask entry points by whether they loaded. glColorMaski is GLES
|
// Detect optional raster/color-mask entry points by whether they loaded. glColorMaski is GLES
|
||||||
// 3.2 core (no extension string), so pointer presence is the reliable signal for all of these.
|
// 3.2 core (no extension string), so pointer presence is the reliable signal for all of these.
|
||||||
@@ -900,6 +907,9 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
GLint maxColorAttachments = 8;
|
GLint maxColorAttachments = 8;
|
||||||
GLint maxClipDistances = 8;
|
GLint maxClipDistances = 8;
|
||||||
GLint maxViewports = 16;
|
GLint maxViewports = 16;
|
||||||
|
GLfloat minFragmentInterpolationOffset = -0.5f;
|
||||||
|
GLfloat maxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
GLint fragmentInterpolationOffsetBits = 4;
|
||||||
glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange);
|
glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange);
|
||||||
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange);
|
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange);
|
||||||
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity);
|
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity);
|
||||||
@@ -950,6 +960,29 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
|
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims);
|
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims);
|
||||||
glesFuncs.glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &viewportSubpixelBits);
|
glesFuncs.glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &viewportSubpixelBits);
|
||||||
|
if (caps.SupportsShaderMultisampleInterpolation && glesFuncs.glGetFloatv) {
|
||||||
|
const auto drainErrors = [&glesFuncs]() {
|
||||||
|
Bool hadError = false;
|
||||||
|
if (glesFuncs.glGetError) {
|
||||||
|
while (glesFuncs.glGetError() != GL_NO_ERROR) hadError = true;
|
||||||
|
}
|
||||||
|
return hadError;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Isolate these optional queries from errors raised by preceding capability
|
||||||
|
// probes, then consume any query error so initialization never leaks it into
|
||||||
|
// the application's first glGetError call.
|
||||||
|
drainErrors();
|
||||||
|
glesFuncs.glGetFloatv(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, &minFragmentInterpolationOffset);
|
||||||
|
glesFuncs.glGetFloatv(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, &maxFragmentInterpolationOffset);
|
||||||
|
glesFuncs.glGetIntegerv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &fragmentInterpolationOffsetBits);
|
||||||
|
if (drainErrors()) {
|
||||||
|
MGLOG_W("Fragment interpolation limit query failed; using OpenGL minimums");
|
||||||
|
minFragmentInterpolationOffset = -0.5f;
|
||||||
|
maxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
fragmentInterpolationOffsetBits = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Only legal to query once the extension has been seen in the loop above, hence not batched
|
// Only legal to query once the extension has been seen in the loop above, hence not batched
|
||||||
// with the unconditional probes: on a driver without it this raises GL_INVALID_ENUM.
|
// with the unconditional probes: on a driver without it this raises GL_INVALID_ENUM.
|
||||||
if (caps.SupportsTextureFilterAnisotropy) {
|
if (caps.SupportsTextureFilterAnisotropy) {
|
||||||
@@ -1007,6 +1040,19 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.ViewportBoundsRangeMin = viewportBoundsRange[0];
|
caps.ViewportBoundsRangeMin = viewportBoundsRange[0];
|
||||||
caps.ViewportBoundsRangeMax = viewportBoundsRange[1];
|
caps.ViewportBoundsRangeMax = viewportBoundsRange[1];
|
||||||
caps.ViewportSubpixelBits = viewportSubpixelBits;
|
caps.ViewportSubpixelBits = viewportSubpixelBits;
|
||||||
|
caps.MinFragmentInterpolationOffset =
|
||||||
|
std::isfinite(minFragmentInterpolationOffset) && minFragmentInterpolationOffset <= -0.5f
|
||||||
|
? minFragmentInterpolationOffset
|
||||||
|
: -0.5f;
|
||||||
|
caps.MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
caps.FragmentInterpolationOffsetBits = 4;
|
||||||
|
if (fragmentInterpolationOffsetBits >= 4 && std::isfinite(maxFragmentInterpolationOffset)) {
|
||||||
|
const Float requiredMaxOffset = 0.5f - std::ldexp(1.0f, -fragmentInterpolationOffsetBits);
|
||||||
|
if (maxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
|
caps.MaxFragmentInterpolationOffset = maxFragmentInterpolationOffset;
|
||||||
|
caps.FragmentInterpolationOffsetBits = fragmentInterpolationOffsetBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
MGLOG_I(" GL_ALIASED_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.AliasedLineWidthRangeMin,
|
MGLOG_I(" GL_ALIASED_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.AliasedLineWidthRangeMin,
|
||||||
caps.AliasedLineWidthRangeMax);
|
caps.AliasedLineWidthRangeMax);
|
||||||
MGLOG_I(" GL_SMOOTH_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.SmoothLineWidthRangeMin,
|
MGLOG_I(" GL_SMOOTH_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.SmoothLineWidthRangeMin,
|
||||||
|
|||||||
@@ -1055,6 +1055,9 @@ namespace MobileGL {
|
|||||||
// SPIRV-Cross's `#extension ... : require` would fail to compile and MobileGL falls back
|
// SPIRV-Cross's `#extension ... : require` would fail to compile and MobileGL falls back
|
||||||
// to stripping the NoPerspective decoration (smooth interpolation) via StripNoPerspectivePass.
|
// to stripping the NoPerspective decoration (smooth interpolation) via StripNoPerspectivePass.
|
||||||
Bool SupportsNoperspectiveInterpolation = false;
|
Bool SupportsNoperspectiveInterpolation = false;
|
||||||
|
// GLES 3.2 core or GL_OES_shader_multisample_interpolation exposes
|
||||||
|
// interpolateAtOffset and the three fragment-offset limit queries.
|
||||||
|
Bool SupportsShaderMultisampleInterpolation = false;
|
||||||
// GL_RENDERER contains "ANGLE".
|
// GL_RENDERER contains "ANGLE".
|
||||||
Bool IsAngleRenderer = false;
|
Bool IsAngleRenderer = false;
|
||||||
// GL_RENDERER contains both "ANGLE" and "llvmpipe".
|
// GL_RENDERER contains both "ANGLE" and "llvmpipe".
|
||||||
@@ -1120,6 +1123,9 @@ namespace MobileGL {
|
|||||||
Float ViewportBoundsRangeMin = 0.0f;
|
Float ViewportBoundsRangeMin = 0.0f;
|
||||||
Float ViewportBoundsRangeMax = 0.0f;
|
Float ViewportBoundsRangeMax = 0.0f;
|
||||||
Int ViewportSubpixelBits = 0;
|
Int ViewportSubpixelBits = 0;
|
||||||
|
Float MinFragmentInterpolationOffset = -0.5f;
|
||||||
|
Float MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
Int FragmentInterpolationOffsetBits = 4;
|
||||||
};
|
};
|
||||||
} // namespace MG_External
|
} // namespace MG_External
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "Loader.h"
|
#include "Loader.h"
|
||||||
|
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace MobileGL::MG_Util::BackendLoader {
|
namespace MobileGL::MG_Util::BackendLoader {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -40,6 +41,25 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
return MaxSampleCountFromFlags(commonFlags);
|
return MaxSampleCountFromFlags(commonFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillFragmentInterpolationLimits(MG_External::VulkanCapabilities& caps,
|
||||||
|
const VkPhysicalDeviceLimits& limits) {
|
||||||
|
caps.MinFragmentInterpolationOffset =
|
||||||
|
std::isfinite(limits.minInterpolationOffset) && limits.minInterpolationOffset <= -0.5f
|
||||||
|
? limits.minInterpolationOffset
|
||||||
|
: -0.5f;
|
||||||
|
|
||||||
|
caps.MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
caps.FragmentInterpolationOffsetBits = 4;
|
||||||
|
const Int bits = static_cast<Int>(limits.subPixelInterpolationOffsetBits);
|
||||||
|
if (bits >= 4 && std::isfinite(limits.maxInterpolationOffset)) {
|
||||||
|
const Float requiredMaxOffset = 0.5f - std::ldexp(1.0f, -bits);
|
||||||
|
if (limits.maxInterpolationOffset >= requiredMaxOffset) {
|
||||||
|
caps.MaxFragmentInterpolationOffset = limits.maxInterpolationOffset;
|
||||||
|
caps.FragmentInterpolationOffsetBits = bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VulkanDynamicFunctions LoadVulkanDynamicFunctions(VkInstance instance) {
|
VulkanDynamicFunctions LoadVulkanDynamicFunctions(VkInstance instance) {
|
||||||
VulkanDynamicFunctions loaded{};
|
VulkanDynamicFunctions loaded{};
|
||||||
if (instance == VK_NULL_HANDLE) {
|
if (instance == VK_NULL_HANDLE) {
|
||||||
@@ -171,6 +191,7 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.ViewportBoundsRangeMin = p.limits.viewportBoundsRange[0];
|
caps.ViewportBoundsRangeMin = p.limits.viewportBoundsRange[0];
|
||||||
caps.ViewportBoundsRangeMax = p.limits.viewportBoundsRange[1];
|
caps.ViewportBoundsRangeMax = p.limits.viewportBoundsRange[1];
|
||||||
caps.ViewportSubpixelBits = static_cast<Int>(p.limits.viewportSubPixelBits);
|
caps.ViewportSubpixelBits = static_cast<Int>(p.limits.viewportSubPixelBits);
|
||||||
|
FillFragmentInterpolationLimits(caps, p.limits);
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures supportedFeatures{};
|
VkPhysicalDeviceFeatures supportedFeatures{};
|
||||||
vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
|
vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
|
||||||
@@ -261,6 +282,7 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.ViewportBoundsRangeMin = properties.limits.viewportBoundsRange[0];
|
caps.ViewportBoundsRangeMin = properties.limits.viewportBoundsRange[0];
|
||||||
caps.ViewportBoundsRangeMax = properties.limits.viewportBoundsRange[1];
|
caps.ViewportBoundsRangeMax = properties.limits.viewportBoundsRange[1];
|
||||||
caps.ViewportSubpixelBits = static_cast<Int>(properties.limits.viewportSubPixelBits);
|
caps.ViewportSubpixelBits = static_cast<Int>(properties.limits.viewportSubPixelBits);
|
||||||
|
FillFragmentInterpolationLimits(caps, properties.limits);
|
||||||
caps.SupportsWideLines = false;
|
caps.SupportsWideLines = false;
|
||||||
// This helper only receives properties, not VkPhysicalDeviceFeatures. Leave optional
|
// This helper only receives properties, not VkPhysicalDeviceFeatures. Leave optional
|
||||||
// stage writes disabled rather than inferring them from descriptor limits alone.
|
// stage writes disabled rather than inferring them from descriptor limits alone.
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ namespace MobileGL {
|
|||||||
Float ViewportBoundsRangeMin = 0.0f;
|
Float ViewportBoundsRangeMin = 0.0f;
|
||||||
Float ViewportBoundsRangeMax = 0.0f;
|
Float ViewportBoundsRangeMax = 0.0f;
|
||||||
Int ViewportSubpixelBits = 0;
|
Int ViewportSubpixelBits = 0;
|
||||||
|
Float MinFragmentInterpolationOffset = -0.5f;
|
||||||
|
Float MaxFragmentInterpolationOffset = 0.4375f;
|
||||||
|
Int FragmentInterpolationOffsetBits = 4;
|
||||||
Bool SupportsWideLines = false;
|
Bool SupportsWideLines = false;
|
||||||
// Storage-image descriptors are limited per stage by
|
// Storage-image descriptors are limited per stage by
|
||||||
// maxPerStageDescriptorStorageImages, but writes/atomics outside compute additionally
|
// maxPerStageDescriptorStorageImages, but writes/atomics outside compute additionally
|
||||||
|
|||||||
Reference in New Issue
Block a user