mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (MG_State, MG_Backend): start TEXTURE_COMPARE_FUNC at LEQUAL
SamplerParameters defaulted compareFunc to ALWAYS, but GL 4.6 core table 23.18 and GLES 3.2 table 21.16 both say the initial value is LEQUAL - for sampler objects and for the sampler state a texture object carries alike. Every freshly created texture and sampler therefore answered GL_ALWAYS to glGetTextureParameteriv(GL_TEXTURE_COMPARE_FUNC). The Vulkan backend had been papering over it: ResolveCompareFunc substituted LESS_EQUAL whenever a depth texture was sampled in compare mode and the func still read ALWAYS, which fixed the rendering but also made an explicitly requested GL_ALWAYS unreachable. With the default corrected that special case is both unnecessary and wrong, so it is gone and the compare op is taken straight from the sampler. Takes direct_state_access.textures_defaults from failing to passing on both backends.
This commit is contained in:
@@ -32,8 +32,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
|
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
|
||||||
if (!gl.glGetError) return;
|
if (!gl.glGetError) return;
|
||||||
while (gl.glGetError() != GL_NO_ERROR) {
|
while (gl.glGetError() != GL_NO_ERROR) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool CheckNoGLError(const MG_External::GLESFunctionsTable& gl) {
|
Bool CheckNoGLError(const MG_External::GLESFunctionsTable& gl) {
|
||||||
@@ -77,8 +76,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bool IsGLESProbeMultisampleTarget(TextureTarget target) {
|
Bool IsGLESProbeMultisampleTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture2DMultisample ||
|
return target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum GetFramebufferAttachment(TextureInternalFormat format) {
|
GLenum GetFramebufferAttachment(TextureInternalFormat format) {
|
||||||
@@ -115,8 +113,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
GLenum normalizedInternalFormat = glFormat;
|
GLenum normalizedInternalFormat = glFormat;
|
||||||
GLenum imageFormat = GL_RGBA;
|
GLenum imageFormat = GL_RGBA;
|
||||||
GLenum imageType = GL_UNSIGNED_BYTE;
|
GLenum imageType = GL_UNSIGNED_BYTE;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(glFormat, PixelFormatNormalizeOptionBit::None,
|
||||||
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
|
&normalizedInternalFormat, &imageFormat, &imageType);
|
||||||
return imageFormat != GL_RED_INTEGER && imageFormat != GL_RG_INTEGER && imageFormat != GL_RGB_INTEGER &&
|
return imageFormat != GL_RED_INTEGER && imageFormat != GL_RG_INTEGER && imageFormat != GL_RGB_INTEGER &&
|
||||||
imageFormat != GL_RGBA_INTEGER && !MG_Util::IsDepthFormatInternalFormat(format) &&
|
imageFormat != GL_RGBA_INTEGER && !MG_Util::IsDepthFormatInternalFormat(format) &&
|
||||||
!MG_Util::IsStencilFormatInternalFormat(format);
|
!MG_Util::IsStencilFormatInternalFormat(format);
|
||||||
@@ -154,9 +152,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
GLESProbeFormatInfo BuildNativeProbeFormatInfo(GLenum requestedInternalFormat) {
|
GLESProbeFormatInfo BuildNativeProbeFormatInfo(GLenum requestedInternalFormat) {
|
||||||
GLESProbeFormatInfo info;
|
GLESProbeFormatInfo info;
|
||||||
info.InternalFormat = requestedInternalFormat;
|
info.InternalFormat = requestedInternalFormat;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(requestedInternalFormat,
|
||||||
requestedInternalFormat, PixelFormatNormalizeOptionBit::None, nullptr, &info.ImageFormat,
|
PixelFormatNormalizeOptionBit::None, nullptr,
|
||||||
&info.ImageType);
|
&info.ImageFormat, &info.ImageType);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,20 +231,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return MG_Util::ConvertGLEnumToString(internalFormat);
|
return MG_Util::ConvertGLEnumToString(internalFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogGLESFormatCaveat(TextureInternalFormat logicalFormat,
|
void LogGLESFormatCaveat(TextureInternalFormat logicalFormat, SizeT targetIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
const GLESProbeFormatInfo& fallbackInfo) {
|
const GLESProbeFormatInfo& fallbackInfo) {
|
||||||
MGLOG_D("Caveat: %s %s not fully supported. Reason: %s. Fallback: %s",
|
MGLOG_D("Caveat: %s %s not fully supported. Reason: %s. Fallback: %s",
|
||||||
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
||||||
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(), fallbackInfo.Reason.c_str(),
|
||||||
fallbackInfo.Reason.c_str(),
|
|
||||||
ConvertFallbackInternalFormatToString(fallbackInfo.InternalFormat).c_str());
|
ConvertFallbackInternalFormatToString(fallbackInfo.InternalFormat).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool BuildFallbackProbeFormatInfo(GLenum requestedInternalFormat,
|
Bool BuildFallbackProbeFormatInfo(GLenum requestedInternalFormat, Flags<PixelFormatNormalizeOptionBit> options,
|
||||||
Flags<PixelFormatNormalizeOptionBit> options,
|
Bool forced, GLESProbeFormatInfo& outInfo) {
|
||||||
Bool forced,
|
|
||||||
GLESProbeFormatInfo& outInfo) {
|
|
||||||
const Flags<PixelFormatNormalizeOptionBit> applicableOptions =
|
const Flags<PixelFormatNormalizeOptionBit> applicableOptions =
|
||||||
MG_Util::TextureFormatProcessor::GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat,
|
MG_Util::TextureFormatProcessor::GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat,
|
||||||
options);
|
options);
|
||||||
@@ -261,8 +255,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return outInfo.InternalFormat != GL_UNKNOWN_MGL;
|
return outInfo.InternalFormat != GL_UNKNOWN_MGL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatCapabilityFlags BuildTextureCapsFromProbe(TextureInternalFormat logicalFormat,
|
FormatCapabilityFlags BuildTextureCapsFromProbe(TextureInternalFormat logicalFormat, TextureTarget target,
|
||||||
TextureTarget target,
|
|
||||||
Bool renderable) {
|
Bool renderable) {
|
||||||
FormatCapabilityFlags caps = GetTextureFeatureCaps(logicalFormat, target);
|
FormatCapabilityFlags caps = GetTextureFeatureCaps(logicalFormat, target);
|
||||||
if (renderable) {
|
if (renderable) {
|
||||||
@@ -276,16 +269,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddFullFormatCaps(FormatCapabilityCache& cache,
|
void AddFullFormatCaps(FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
SizeT formatIndex,
|
|
||||||
FormatCapabilityFlags caps) {
|
FormatCapabilityFlags caps) {
|
||||||
cache.FullCaps[targetIndex][formatIndex] |= caps;
|
cache.FullCaps[targetIndex][formatIndex] |= caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool AddCaveatFormatCaps(FormatCapabilityCache& cache,
|
Bool AddCaveatFormatCaps(FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
SizeT formatIndex,
|
|
||||||
FormatCapabilityFlags caps) {
|
FormatCapabilityFlags caps) {
|
||||||
Bool added = false;
|
Bool added = false;
|
||||||
for (FormatCapability capability : kReportedFormatCapabilities) {
|
for (FormatCapability capability : kReportedFormatCapabilities) {
|
||||||
@@ -299,8 +288,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Int GetGLESFormatMaxSamples(const MG_External::GLESCapabilities& capabilities,
|
Int GetGLESFormatMaxSamples(const MG_External::GLESCapabilities& capabilities,
|
||||||
TextureInternalFormat logicalFormat,
|
TextureInternalFormat logicalFormat, GLenum imageFormat) {
|
||||||
GLenum imageFormat) {
|
|
||||||
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
||||||
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
|
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
|
||||||
const Bool isInteger = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
|
const Bool isInteger = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
|
||||||
@@ -314,10 +302,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return capabilities.MaxColorTextureSamples;
|
return capabilities.MaxColorTextureSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeFramebufferCompletenessForTexture(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeFramebufferCompletenessForTexture(const MG_External::GLESFunctionsTable& gl, TextureTarget target,
|
||||||
TextureTarget target,
|
GLuint texture, TextureInternalFormat format) {
|
||||||
GLuint texture,
|
|
||||||
TextureInternalFormat format) {
|
|
||||||
GLuint framebuffer = 0;
|
GLuint framebuffer = 0;
|
||||||
GLint prevFramebuffer = 0;
|
GLint prevFramebuffer = 0;
|
||||||
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glCheckFramebufferStatus ||
|
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glCheckFramebufferStatus ||
|
||||||
@@ -399,8 +385,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl, GLuint renderbuffer,
|
||||||
GLuint renderbuffer,
|
|
||||||
TextureInternalFormat format) {
|
TextureInternalFormat format) {
|
||||||
GLuint framebuffer = 0;
|
GLuint framebuffer = 0;
|
||||||
GLint prevFramebuffer = 0;
|
GLint prevFramebuffer = 0;
|
||||||
@@ -468,16 +453,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TextureTarget::Texture3D:
|
case TextureTarget::Texture3D:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 2, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 2, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
case TextureTarget::Texture2DArray:
|
case TextureTarget::Texture2DArray:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 1, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 1, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
case TextureTarget::TextureCubeMapArray:
|
case TextureTarget::TextureCubeMapArray:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 6, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 6, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -498,11 +483,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeRenderbuffer(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeRenderbuffer(const MG_External::GLESFunctionsTable& gl, GLenum internalFormat,
|
||||||
GLenum internalFormat,
|
TextureInternalFormat logicalFormat, Bool multisample, Int samples) {
|
||||||
TextureInternalFormat logicalFormat,
|
|
||||||
Bool multisample,
|
|
||||||
Int samples) {
|
|
||||||
if (!gl.glGenRenderbuffers || !gl.glBindRenderbuffer || !gl.glDeleteRenderbuffers) {
|
if (!gl.glGenRenderbuffers || !gl.glBindRenderbuffer || !gl.glDeleteRenderbuffers) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -524,17 +506,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
gl.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
|
gl.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
|
||||||
}
|
}
|
||||||
const Bool created = CheckNoGLError(gl);
|
const Bool created = CheckNoGLError(gl);
|
||||||
const Bool complete = created && ProbeFramebufferCompletenessForRenderbuffer(gl, renderbuffer, logicalFormat);
|
const Bool complete =
|
||||||
|
created && ProbeFramebufferCompletenessForRenderbuffer(gl, renderbuffer, logicalFormat);
|
||||||
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
|
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
|
||||||
gl.glDeleteRenderbuffers(1, &renderbuffer);
|
gl.glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
ClearGLErrors(gl);
|
ClearGLErrors(gl);
|
||||||
return complete;
|
return complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Int> ProbeRenderbufferSampleCounts(const MG_External::GLESFunctionsTable& gl,
|
Vector<Int> ProbeRenderbufferSampleCounts(const MG_External::GLESFunctionsTable& gl, GLenum internalFormat,
|
||||||
GLenum internalFormat,
|
TextureInternalFormat logicalFormat, Int maxSamples) {
|
||||||
TextureInternalFormat logicalFormat,
|
|
||||||
Int maxSamples) {
|
|
||||||
Vector<Int> sampleCounts;
|
Vector<Int> sampleCounts;
|
||||||
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
|
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
|
||||||
if (ProbeRenderbuffer(gl, internalFormat, logicalFormat, true, samples)) {
|
if (ProbeRenderbuffer(gl, internalFormat, logicalFormat, true, samples)) {
|
||||||
@@ -589,8 +570,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
hasForcedFallback = BuildFallbackProbeFormatInfo(
|
hasForcedFallback = BuildFallbackProbeFormatInfo(
|
||||||
requestedInternalFormat, forcedOptions | targetOptions, true, fallbackInfo);
|
requestedInternalFormat, forcedOptions | targetOptions, true, fallbackInfo);
|
||||||
if (!hasForcedFallback) {
|
if (!hasForcedFallback) {
|
||||||
BuildFallbackProbeFormatInfo(requestedInternalFormat, driverOptions | targetOptions,
|
BuildFallbackProbeFormatInfo(requestedInternalFormat, driverOptions | targetOptions, false,
|
||||||
false, fallbackInfo);
|
fallbackInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,9 +604,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
ProbeTexture(gl, probeTarget, fallbackInfo.InternalFormat, fallbackInfo.ImageFormat,
|
ProbeTexture(gl, probeTarget, fallbackInfo.InternalFormat, fallbackInfo.ImageFormat,
|
||||||
fallbackInfo.ImageType, logicalFormat, &fallbackRenderable);
|
fallbackInfo.ImageType, logicalFormat, &fallbackRenderable);
|
||||||
if (fallbackCreated) {
|
if (fallbackCreated) {
|
||||||
if (AddCaveatFormatCaps(cache, targetIndex, formatIndex,
|
if (AddCaveatFormatCaps(
|
||||||
BuildTextureCapsFromProbe(logicalFormat, target,
|
cache, targetIndex, formatIndex,
|
||||||
fallbackRenderable))) {
|
BuildTextureCapsFromProbe(logicalFormat, target, fallbackRenderable))) {
|
||||||
LogGLESFormatCaveat(logicalFormat, targetIndex, fallbackInfo);
|
LogGLESFormatCaveat(logicalFormat, targetIndex, fallbackInfo);
|
||||||
}
|
}
|
||||||
if (IsGLESProbeMultisampleTarget(target)) {
|
if (IsGLESProbeMultisampleTarget(target)) {
|
||||||
@@ -676,7 +657,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
.ExtraVendor = Nullopt, // Extra vendor
|
.ExtraVendor = Nullopt, // Extra vendor
|
||||||
.RendererGLInfo =
|
.RendererGLInfo =
|
||||||
{
|
{
|
||||||
.TargetGLVersion = {3, 3, 0}, // GL target version
|
.TargetGLVersion = {4, 0, 0}, // GL 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.
|
||||||
@@ -707,8 +688,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PopulateFormatCapabilities(const MG_External::GLESFunctionsTable& gl,
|
void PopulateFormatCapabilities(const MG_External::GLESFunctionsTable& gl,
|
||||||
const MG_External::GLESCapabilities& capabilities,
|
const MG_External::GLESCapabilities& capabilities, FormatCapabilityCache& cache) {
|
||||||
FormatCapabilityCache& cache) {
|
|
||||||
PopulateFormatCapabilitiesImpl(gl, capabilities, cache);
|
PopulateFormatCapabilitiesImpl(gl, capabilities, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,10 +752,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((handle.Backend != WindowBackend::Android &&
|
if ((handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::MetalLayer && handle.Backend != WindowBackend::Win32) ||
|
||||||
handle.Backend != WindowBackend::MetalLayer &&
|
|
||||||
handle.Backend != WindowBackend::Win32) ||
|
|
||||||
!handle.Handle) {
|
!handle.Handle) {
|
||||||
MGLOG_E("DirectGLES backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
MGLOG_E("DirectGLES backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
@@ -894,14 +872,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vector<GLExtension> BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported) {
|
Vector<GLExtension> BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported) {
|
||||||
Vector<GLExtension> extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
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_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store, E_GL_ARB_program_interface_query,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
E_GL_ARB_framebuffer_object, E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample, E_GL_ARB_clear_texture,
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample,
|
E_GL_ARB_direct_state_access, E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
||||||
E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
|
||||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
|
||||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind,
|
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind,
|
||||||
E_GL_ARB_shading_language_420pack, E_GL_ARB_vertex_attrib_binding,
|
E_GL_ARB_shading_language_420pack, E_GL_ARB_vertex_attrib_binding,
|
||||||
// Both are core from GL 3.2/3.3 on and implemented here for
|
// Both are core from GL 3.2/3.3 on and implemented here for
|
||||||
@@ -909,8 +885,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// only reaches them through the extension string - the CTS
|
// only reaches them through the extension string - the CTS
|
||||||
// picks a whole different shader for draw_buffers without
|
// picks a whole different shader for draw_buffers without
|
||||||
// explicit_attrib_location. DirectVulkan advertises both.
|
// explicit_attrib_location. DirectVulkan advertises both.
|
||||||
E_GL_ARB_explicit_attrib_location, E_GL_ARB_texture_multisample,
|
E_GL_ARB_explicit_attrib_location, E_GL_ARB_texture_multisample, E_GL_ARB_shader_image_size,
|
||||||
E_GL_ARB_shader_image_size,
|
|
||||||
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
||||||
// extension explicitly permits. It is also the only thing that
|
// extension explicitly permits. It is also the only thing that
|
||||||
// exposes glProgramParameteri before GL 4.1.
|
// exposes glProgramParameteri before GL 4.1.
|
||||||
@@ -1052,8 +1027,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return m_dynamicParameters;
|
return m_dynamicParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendObject_DirectGLES::ApplyGLESCapabilitiesForTesting(
|
void BackendObject_DirectGLES::ApplyGLESCapabilitiesForTesting(const MG_External::GLESCapabilities& capabilities) {
|
||||||
const MG_External::GLESCapabilities& capabilities) {
|
|
||||||
m_GLESCapabilities = capabilities;
|
m_GLESCapabilities = capabilities;
|
||||||
UpdateDynamicBackendParameters();
|
UpdateDynamicBackendParameters();
|
||||||
}
|
}
|
||||||
@@ -1117,8 +1091,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
m_dynamicParameters.TextureBufferOffsetAlignment = m_GLESCapabilities.TextureBufferOffsetAlignment;
|
m_dynamicParameters.TextureBufferOffsetAlignment = m_GLESCapabilities.TextureBufferOffsetAlignment;
|
||||||
m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings;
|
m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings;
|
||||||
m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize;
|
m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize;
|
||||||
const Int maxSupportedTextureUnits =
|
const Int maxSupportedTextureUnits = static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
|
||||||
m_dynamicParameters.MaxImageUnits =
|
m_dynamicParameters.MaxImageUnits =
|
||||||
std::max(std::min(m_GLESCapabilities.MaxImageUnits, maxSupportedTextureUnits), 0);
|
std::max(std::min(m_GLESCapabilities.MaxImageUnits, maxSupportedTextureUnits), 0);
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_GLESCapabilities.MaxCombinedImageUniforms, 0);
|
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_GLESCapabilities.MaxCombinedImageUniforms, 0);
|
||||||
@@ -1126,8 +1099,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return std::min({std::max(stageLimit, 0), m_dynamicParameters.MaxImageUnits,
|
return std::min({std::max(stageLimit, 0), m_dynamicParameters.MaxImageUnits,
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms});
|
m_dynamicParameters.MaxCombinedImageUniforms});
|
||||||
};
|
};
|
||||||
m_dynamicParameters.MaxVertexImageUniforms =
|
m_dynamicParameters.MaxVertexImageUniforms = clampStageImageUniforms(m_GLESCapabilities.MaxVertexImageUniforms);
|
||||||
clampStageImageUniforms(m_GLESCapabilities.MaxVertexImageUniforms);
|
|
||||||
m_dynamicParameters.MaxGeometryImageUniforms =
|
m_dynamicParameters.MaxGeometryImageUniforms =
|
||||||
clampStageImageUniforms(m_GLESCapabilities.MaxGeometryImageUniforms);
|
clampStageImageUniforms(m_GLESCapabilities.MaxGeometryImageUniforms);
|
||||||
m_dynamicParameters.MaxFragmentImageUniforms =
|
m_dynamicParameters.MaxFragmentImageUniforms =
|
||||||
@@ -1157,8 +1129,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const Float requiredMaxOffset =
|
const Float requiredMaxOffset =
|
||||||
0.5f - std::ldexp(1.0f, -m_GLESCapabilities.FragmentInterpolationOffsetBits);
|
0.5f - std::ldexp(1.0f, -m_GLESCapabilities.FragmentInterpolationOffsetBits);
|
||||||
if (m_GLESCapabilities.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
if (m_GLESCapabilities.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
m_dynamicParameters.MaxFragmentInterpolationOffset =
|
m_dynamicParameters.MaxFragmentInterpolationOffset = m_GLESCapabilities.MaxFragmentInterpolationOffset;
|
||||||
m_GLESCapabilities.MaxFragmentInterpolationOffset;
|
|
||||||
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
||||||
m_GLESCapabilities.FragmentInterpolationOffsetBits;
|
m_GLESCapabilities.FragmentInterpolationOffsetBits;
|
||||||
}
|
}
|
||||||
@@ -1167,9 +1138,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
||||||
|
|
||||||
const auto containsAny = [](const String& haystack, std::initializer_list<const char*> needles) {
|
const auto containsAny = [](const String& haystack, std::initializer_list<const char*> needles) {
|
||||||
return std::any_of(needles.begin(), needles.end(), [&](const char* needle) {
|
return std::any_of(needles.begin(), needles.end(),
|
||||||
return haystack.find(needle) != String::npos;
|
[&](const char* needle) { return haystack.find(needle) != String::npos; });
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const String vendorAndRenderer =
|
const String vendorAndRenderer =
|
||||||
m_GLESCapabilities.GLESVendorString + " " + m_GLESCapabilities.GLESRendererString;
|
m_GLESCapabilities.GLESVendorString + " " + m_GLESCapabilities.GLESRendererString;
|
||||||
|
|||||||
@@ -41,13 +41,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool IsLayeredTarget(TextureTarget target) {
|
Bool IsLayeredTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture3D || target == TextureTarget::Texture1DArray ||
|
return target == TextureTarget::Texture3D || target == TextureTarget::Texture1DArray ||
|
||||||
target == TextureTarget::Texture2DArray || target == TextureTarget::TextureCubeMap ||
|
target == TextureTarget::Texture2DArray || target == TextureTarget::TextureCubeMap ||
|
||||||
target == TextureTarget::TextureCubeMapArray ||
|
target == TextureTarget::TextureCubeMapArray || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsMultisampleTarget(TextureTarget target) {
|
Bool IsMultisampleTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture2DMultisample ||
|
return target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsTextureBufferTarget(TextureTarget target) {
|
Bool IsTextureBufferTarget(TextureTarget target) {
|
||||||
@@ -59,8 +57,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
GLenum normalizedInternalFormat = glFormat;
|
GLenum normalizedInternalFormat = glFormat;
|
||||||
GLenum imageFormat = GL_RGBA;
|
GLenum imageFormat = GL_RGBA;
|
||||||
GLenum imageType = GL_UNSIGNED_BYTE;
|
GLenum imageType = GL_UNSIGNED_BYTE;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(glFormat, PixelFormatNormalizeOptionBit::None,
|
||||||
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
|
&normalizedInternalFormat, &imageFormat, &imageType);
|
||||||
return imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || imageFormat == GL_RGB_INTEGER ||
|
return imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || imageFormat == GL_RGB_INTEGER ||
|
||||||
imageFormat == GL_RGBA_INTEGER;
|
imageFormat == GL_RGBA_INTEGER;
|
||||||
}
|
}
|
||||||
@@ -81,8 +79,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatCapabilityFlags BuildVulkanCaps(TextureInternalFormat logicalFormat,
|
FormatCapabilityFlags BuildVulkanCaps(TextureInternalFormat logicalFormat, TextureTarget target,
|
||||||
TextureTarget target,
|
|
||||||
VkFormatFeatureFlags features) {
|
VkFormatFeatureFlags features) {
|
||||||
FormatCapabilityFlags caps;
|
FormatCapabilityFlags caps;
|
||||||
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
||||||
@@ -101,8 +98,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const Bool sampled = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
|
const Bool sampled = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
|
||||||
const Bool linearFilter = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0;
|
const Bool linearFilter = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0;
|
||||||
const Bool colorRenderable = (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
const Bool colorRenderable = (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
||||||
const Bool depthStencilRenderable =
|
const Bool depthStencilRenderable = (features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
|
||||||
(features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
|
|
||||||
const Bool renderable = (isDepth || isStencil) ? depthStencilRenderable : colorRenderable;
|
const Bool renderable = (isDepth || isStencil) ? depthStencilRenderable : colorRenderable;
|
||||||
|
|
||||||
if (sampled || renderable) {
|
if (sampled || renderable) {
|
||||||
@@ -199,18 +195,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
Bool HasNewCaveatFormatCaps(FormatCapabilityFlags nativeCaps, FormatCapabilityFlags fallbackCaps) {
|
Bool HasNewCaveatFormatCaps(FormatCapabilityFlags nativeCaps, FormatCapabilityFlags fallbackCaps) {
|
||||||
for (FormatCapability capability : kReportedFormatCapabilities) {
|
for (FormatCapability capability : kReportedFormatCapabilities) {
|
||||||
if (HasFormatCapability(fallbackCaps, capability) &&
|
if (HasFormatCapability(fallbackCaps, capability) && !HasFormatCapability(nativeCaps, capability)) {
|
||||||
!HasFormatCapability(nativeCaps, capability)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogVulkanFormatCaveat(TextureInternalFormat logicalFormat,
|
void LogVulkanFormatCaveat(TextureInternalFormat logicalFormat, SizeT targetIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
TextureInternalFormat fallbackFormat) {
|
TextureInternalFormat fallbackFormat) {
|
||||||
MGLOG_D("Caveat: %s %s not fully supported. Reason: native Vulkan format is not fully supported. Fallback: %s",
|
MGLOG_D(
|
||||||
|
"Caveat: %s %s not fully supported. Reason: native Vulkan format is not fully supported. Fallback: %s",
|
||||||
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
||||||
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
||||||
MG_Util::ConvertTextureInternalFormatToString(fallbackFormat).c_str());
|
MG_Util::ConvertTextureInternalFormatToString(fallbackFormat).c_str());
|
||||||
@@ -257,14 +252,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
|
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
|
||||||
const auto target = static_cast<TextureTarget>(targetIndex);
|
const auto target = static_cast<TextureTarget>(targetIndex);
|
||||||
const VkFormatFeatureFlags nativeFeatures =
|
const VkFormatFeatureFlags nativeFeatures = IsTextureBufferTarget(target)
|
||||||
IsTextureBufferTarget(target) ? nativeProperties.bufferFeatures
|
? nativeProperties.bufferFeatures
|
||||||
: nativeProperties.optimalTilingFeatures;
|
: nativeProperties.optimalTilingFeatures;
|
||||||
FormatCapabilityFlags nativeCaps = BuildVulkanCaps(logicalFormat, target, nativeFeatures);
|
FormatCapabilityFlags nativeCaps = BuildVulkanCaps(logicalFormat, target, nativeFeatures);
|
||||||
cache.FullCaps[targetIndex][formatIndex] |= nativeCaps;
|
cache.FullCaps[targetIndex][formatIndex] |= nativeCaps;
|
||||||
|
|
||||||
const VkFormatFeatureFlags fallbackFeatures =
|
const VkFormatFeatureFlags fallbackFeatures = IsTextureBufferTarget(target)
|
||||||
IsTextureBufferTarget(target) ? fallbackProperties.bufferFeatures
|
? fallbackProperties.bufferFeatures
|
||||||
: fallbackProperties.optimalTilingFeatures;
|
: fallbackProperties.optimalTilingFeatures;
|
||||||
FormatCapabilityFlags fallbackCaps = BuildVulkanCaps(logicalFormat, target, fallbackFeatures);
|
FormatCapabilityFlags fallbackCaps = BuildVulkanCaps(logicalFormat, target, fallbackFeatures);
|
||||||
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
||||||
@@ -300,9 +295,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
cache.FullCaps[renderbufferTargetIndex][formatIndex] |= renderbufferCaps;
|
cache.FullCaps[renderbufferTargetIndex][formatIndex] |= renderbufferCaps;
|
||||||
|
|
||||||
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
||||||
FormatCapabilityFlags fallbackRenderbufferCaps =
|
FormatCapabilityFlags fallbackRenderbufferCaps = BuildVulkanCaps(
|
||||||
BuildVulkanCaps(logicalFormat, TextureTarget::Texture2D,
|
logicalFormat, TextureTarget::Texture2D, fallbackProperties.optimalTilingFeatures);
|
||||||
fallbackProperties.optimalTilingFeatures);
|
|
||||||
fallbackRenderbufferCaps &= FormatCapability::Creatable;
|
fallbackRenderbufferCaps &= FormatCapability::Creatable;
|
||||||
if ((fallbackProperties.optimalTilingFeatures &
|
if ((fallbackProperties.optimalTilingFeatures &
|
||||||
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) !=
|
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) !=
|
||||||
@@ -311,8 +305,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
fallbackRenderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
|
fallbackRenderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
|
||||||
}
|
}
|
||||||
cache.CaveatCaps[renderbufferTargetIndex][formatIndex] |= fallbackRenderbufferCaps;
|
cache.CaveatCaps[renderbufferTargetIndex][formatIndex] |= fallbackRenderbufferCaps;
|
||||||
if (fallbackLogicalFormat &&
|
if (fallbackLogicalFormat && HasNewCaveatFormatCaps(renderbufferCaps, fallbackRenderbufferCaps)) {
|
||||||
HasNewCaveatFormatCaps(renderbufferCaps, fallbackRenderbufferCaps)) {
|
|
||||||
LogVulkanFormatCaveat(logicalFormat, renderbufferTargetIndex, *fallbackLogicalFormat);
|
LogVulkanFormatCaveat(logicalFormat, renderbufferTargetIndex, *fallbackLogicalFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,8 +322,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
void PopulateFormatCapabilities(VkPhysicalDevice physicalDevice,
|
void PopulateFormatCapabilities(VkPhysicalDevice physicalDevice,
|
||||||
PFN_vkGetPhysicalDeviceFormatProperties getFormatProperties,
|
PFN_vkGetPhysicalDeviceFormatProperties getFormatProperties,
|
||||||
const MG_External::VulkanCapabilities& capabilities,
|
const MG_External::VulkanCapabilities& capabilities, FormatCapabilityCache& cache) {
|
||||||
FormatCapabilityCache& cache) {
|
|
||||||
PopulateFormatCapabilitiesImpl(physicalDevice, getFormatProperties, capabilities, cache);
|
PopulateFormatCapabilitiesImpl(physicalDevice, getFormatProperties, capabilities, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,10 +402,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MGLOG_E("DirectVulkan backend not initialized");
|
MGLOG_E("DirectVulkan backend not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!handle.Handle || (handle.Backend != WindowBackend::Android &&
|
if (!handle.Handle || (handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::MetalLayer && handle.Backend != WindowBackend::Win32)) {
|
||||||
handle.Backend != WindowBackend::MetalLayer &&
|
|
||||||
handle.Backend != WindowBackend::Win32)) {
|
|
||||||
MGLOG_E("DirectVulkan backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
MGLOG_E("DirectVulkan backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -504,33 +494,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.RendererName = "Magma",
|
.RendererName = "Magma",
|
||||||
.BackendName = "Direct (Vulkan)",
|
.BackendName = "Direct (Vulkan)",
|
||||||
.ExtraVendor = Nullopt,
|
.ExtraVendor = Nullopt,
|
||||||
.RendererGLInfo =
|
.RendererGLInfo = {.TargetGLVersion = {4, 0, 0},
|
||||||
{
|
|
||||||
.TargetGLVersion = {3, 3, 0},
|
|
||||||
.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.
|
||||||
.Extensions = BuildAdvertisedExtensions(false, false, false),
|
.Extensions = BuildAdvertisedExtensions(false, false, false),
|
||||||
.IsCompatibilityProfile = false
|
.IsCompatibilityProfile = false},
|
||||||
},
|
|
||||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}};
|
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}};
|
||||||
return rendererInfo;
|
return rendererInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<GLExtension> BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported,
|
Vector<GLExtension> BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported,
|
||||||
Bool anisotropicFilteringSupported) {
|
Bool anisotropicFilteringSupported) {
|
||||||
Vector<GLExtension> extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
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_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store, E_GL_ARB_program_interface_query,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
E_GL_ARB_framebuffer_object, E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
||||||
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_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
E_GL_ARB_texture_storage_multisample, E_GL_ARB_texture_multisample, E_GL_ARB_clear_texture,
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample,
|
E_GL_ARB_direct_state_access, E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64, E_GL_KHR_debug,
|
||||||
E_GL_ARB_texture_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
|
||||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64, E_GL_KHR_debug,
|
|
||||||
E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind, E_GL_ARB_shading_language_420pack,
|
E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind, E_GL_ARB_shading_language_420pack,
|
||||||
E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size,
|
E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size, E_GL_ARB_explicit_attrib_location,
|
||||||
E_GL_ARB_explicit_attrib_location,
|
|
||||||
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
||||||
// extension explicitly permits. It is also the only thing that
|
// extension explicitly permits. It is also the only thing that
|
||||||
// exposes glProgramParameteri before GL 4.1.
|
// exposes glProgramParameteri before GL 4.1.
|
||||||
@@ -752,8 +736,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.MaxIntegerSamples = m_vulkanCaps.MaxIntegerSamples;
|
m_dynamicParameters.MaxIntegerSamples = m_vulkanCaps.MaxIntegerSamples;
|
||||||
m_dynamicParameters.MaxSamples = m_vulkanCaps.MaxSamples;
|
m_dynamicParameters.MaxSamples = m_vulkanCaps.MaxSamples;
|
||||||
m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords;
|
m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords;
|
||||||
const Int maxSupportedTextureUnits =
|
const Int maxSupportedTextureUnits = static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
|
||||||
// GL_MAX_TEXTURE_IMAGE_UNITS is a *per-stage* sampler limit. Adreno/Qualcomm report a huge
|
// GL_MAX_TEXTURE_IMAGE_UNITS is a *per-stage* sampler limit. Adreno/Qualcomm report a huge
|
||||||
// maxPerStageDescriptorSampledImages (descriptor-indexing scale), so clamping it only to our
|
// maxPerStageDescriptorSampledImages (descriptor-indexing scale), so clamping it only to our
|
||||||
// combined array capacity (192) still advertises 192 per stage. Host code treats this value as
|
// combined array capacity (192) still advertises 192 per stage. Host code treats this value as
|
||||||
@@ -763,8 +746,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// limits while keeping the combined limit at our texture-unit array capacity.
|
// limits while keeping the combined limit at our texture-unit array capacity.
|
||||||
constexpr Int maxPerStageTextureUnits =
|
constexpr Int maxPerStageTextureUnits =
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS);
|
static_cast<Int>(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS);
|
||||||
m_dynamicParameters.MaxTextureImageUnits =
|
m_dynamicParameters.MaxTextureImageUnits = std::min(m_vulkanCaps.MaxTextureImageUnits, maxPerStageTextureUnits);
|
||||||
std::min(m_vulkanCaps.MaxTextureImageUnits, maxPerStageTextureUnits);
|
|
||||||
m_dynamicParameters.MaxVertexTextureImageUnits =
|
m_dynamicParameters.MaxVertexTextureImageUnits =
|
||||||
std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxPerStageTextureUnits);
|
std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxPerStageTextureUnits);
|
||||||
m_dynamicParameters.MaxComputeTextureImageUnits =
|
m_dynamicParameters.MaxComputeTextureImageUnits =
|
||||||
@@ -773,9 +755,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
std::min(m_vulkanCaps.MaxCombinedTextureImageUnits, maxSupportedTextureUnits);
|
std::min(m_vulkanCaps.MaxCombinedTextureImageUnits, maxSupportedTextureUnits);
|
||||||
// Never advertise more attributes than the state layer can store: the current-value array and
|
// Never advertise more attributes than the state layer can store: the current-value array and
|
||||||
// the Uint32 attribute masks the draw path passes around are both bounded by MAX_VERTEX_ATTRIBS.
|
// the Uint32 attribute masks the draw path passes around are both bounded by MAX_VERTEX_ATTRIBS.
|
||||||
m_dynamicParameters.MaxVertexAttribs =
|
m_dynamicParameters.MaxVertexAttribs = std::min(
|
||||||
std::min(m_vulkanCaps.MaxVertexAttribs,
|
m_vulkanCaps.MaxVertexAttribs, static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
||||||
static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
|
||||||
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_vulkanCaps.MaxComputeShaderStorageBlocks;
|
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_vulkanCaps.MaxComputeShaderStorageBlocks;
|
||||||
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_vulkanCaps.MaxCombinedShaderStorageBlocks;
|
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_vulkanCaps.MaxCombinedShaderStorageBlocks;
|
||||||
m_dynamicParameters.MaxComputeUniformBlocks = m_vulkanCaps.MaxComputeUniformBlocks;
|
m_dynamicParameters.MaxComputeUniformBlocks = m_vulkanCaps.MaxComputeUniformBlocks;
|
||||||
@@ -785,8 +766,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
||||||
m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings;
|
m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings;
|
||||||
m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize;
|
m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize;
|
||||||
m_dynamicParameters.MaxImageUnits =
|
m_dynamicParameters.MaxImageUnits = std::max(std::min(m_vulkanCaps.MaxImageUnits, maxSupportedTextureUnits), 0);
|
||||||
std::max(std::min(m_vulkanCaps.MaxImageUnits, maxSupportedTextureUnits), 0);
|
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_vulkanCaps.MaxCombinedImageUniforms, 0);
|
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_vulkanCaps.MaxCombinedImageUniforms, 0);
|
||||||
const Int maxPerStageImageUniforms =
|
const Int maxPerStageImageUniforms =
|
||||||
std::min(m_dynamicParameters.MaxImageUnits, m_dynamicParameters.MaxCombinedImageUniforms);
|
std::min(m_dynamicParameters.MaxImageUnits, m_dynamicParameters.MaxCombinedImageUniforms);
|
||||||
@@ -803,8 +783,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_vulkanCaps.SupportsFragmentStoresAndAtomics ? maxPerStageImageUniforms : 0;
|
m_vulkanCaps.SupportsFragmentStoresAndAtomics ? maxPerStageImageUniforms : 0;
|
||||||
m_dynamicParameters.MaxComputeImageUniforms =
|
m_dynamicParameters.MaxComputeImageUniforms =
|
||||||
std::min(std::max(m_vulkanCaps.MaxComputeImageUniforms, 0), maxPerStageImageUniforms);
|
std::min(std::max(m_vulkanCaps.MaxComputeImageUniforms, 0), maxPerStageImageUniforms);
|
||||||
const Int maxSupportedDrawBuffers =
|
const Int maxSupportedDrawBuffers = static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);
|
||||||
static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);
|
|
||||||
m_dynamicParameters.MaxDrawBuffers = std::min(m_vulkanCaps.MaxDrawBuffers, maxSupportedDrawBuffers);
|
m_dynamicParameters.MaxDrawBuffers = std::min(m_vulkanCaps.MaxDrawBuffers, maxSupportedDrawBuffers);
|
||||||
m_dynamicParameters.MaxColorAttachments = std::min(m_vulkanCaps.MaxColorAttachments, maxSupportedDrawBuffers);
|
m_dynamicParameters.MaxColorAttachments = std::min(m_vulkanCaps.MaxColorAttachments, maxSupportedDrawBuffers);
|
||||||
m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances;
|
m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances;
|
||||||
@@ -823,12 +802,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
||||||
if (m_vulkanCaps.FragmentInterpolationOffsetBits >= 4 &&
|
if (m_vulkanCaps.FragmentInterpolationOffsetBits >= 4 &&
|
||||||
std::isfinite(m_vulkanCaps.MaxFragmentInterpolationOffset)) {
|
std::isfinite(m_vulkanCaps.MaxFragmentInterpolationOffset)) {
|
||||||
const Float requiredMaxOffset =
|
const Float requiredMaxOffset = 0.5f - std::ldexp(1.0f, -m_vulkanCaps.FragmentInterpolationOffsetBits);
|
||||||
0.5f - std::ldexp(1.0f, -m_vulkanCaps.FragmentInterpolationOffsetBits);
|
|
||||||
if (m_vulkanCaps.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
if (m_vulkanCaps.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
m_dynamicParameters.MaxFragmentInterpolationOffset = m_vulkanCaps.MaxFragmentInterpolationOffset;
|
m_dynamicParameters.MaxFragmentInterpolationOffset = m_vulkanCaps.MaxFragmentInterpolationOffset;
|
||||||
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
m_dynamicParameters.FragmentInterpolationOffsetBits = m_vulkanCaps.FragmentInterpolationOffsetBits;
|
||||||
m_vulkanCaps.FragmentInterpolationOffsetBits;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
||||||
@@ -837,7 +814,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
||||||
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
|
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
|
||||||
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
|
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
|
||||||
m_dynamicParameters.SubgroupSupportedFeatures = mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
|
m_dynamicParameters.SubgroupSupportedFeatures =
|
||||||
|
mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
|
||||||
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
|
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
|
||||||
} else {
|
} else {
|
||||||
m_dynamicParameters.SubgroupSize = 0;
|
m_dynamicParameters.SubgroupSize = 0;
|
||||||
@@ -847,8 +825,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
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, m_dynamicParameters.MaxShaderStorageBlockSize);
|
||||||
m_dynamicParameters.MaxShaderStorageBlockSize);
|
|
||||||
}
|
}
|
||||||
switch (m_vulkanCaps.VendorId) {
|
switch (m_vulkanCaps.VendorId) {
|
||||||
case 0x5143u: // VK_VENDOR_ID: Qualcomm
|
case 0x5143u: // VK_VENDOR_ID: Qualcomm
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &maxAnisotropy, sizeof(maxAnisotropy)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &maxAnisotropy, sizeof(maxAnisotropy)));
|
||||||
const auto compareMode = sampler.GetCompareMode();
|
const auto compareMode = sampler.GetCompareMode();
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode)));
|
||||||
const auto compareFunc = ResolveCompareFunc(sampler, texture);
|
const auto compareFunc = sampler.GetSamplerCompareFunc();
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
|
||||||
const auto borderColor = ResolveVkBorderColor(sampler, texture);
|
const auto borderColor = ResolveVkBorderColor(sampler, texture);
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor)));
|
||||||
@@ -207,7 +207,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
samplerInfo.anisotropyEnable = maxAnisotropy > 1.0f ? VK_TRUE : VK_FALSE;
|
samplerInfo.anisotropyEnable = maxAnisotropy > 1.0f ? VK_TRUE : VK_FALSE;
|
||||||
samplerInfo.maxAnisotropy = maxAnisotropy;
|
samplerInfo.maxAnisotropy = maxAnisotropy;
|
||||||
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
|
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
|
||||||
samplerInfo.compareOp = ToVkCompareOp(ResolveCompareFunc(sampler, texture));
|
samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc());
|
||||||
// Must match BuildSamplerKey's resolution exactly.
|
// Must match BuildSamplerKey's resolution exactly.
|
||||||
samplerInfo.maxLod = ResolveSingleLevelMaxLod(sampler, singleLevelView);
|
samplerInfo.maxLod = ResolveSingleLevelMaxLod(sampler, singleLevelView);
|
||||||
samplerInfo.minLod = ResolveEffectiveMinLod(sampler, samplerInfo.maxLod);
|
samplerInfo.minLod = ResolveEffectiveMinLod(sampler, samplerInfo.maxLod);
|
||||||
@@ -281,17 +281,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SamplerCompareFunc VkSamplerManager::ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
|
|
||||||
const MG_State::GLState::ITextureObject& texture) {
|
|
||||||
const auto compareFunc = sampler.GetSamplerCompareFunc();
|
|
||||||
if (sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture &&
|
|
||||||
IsDepthTextureFormat(texture.GetFormat()) && compareFunc == SamplerCompareFunc::Always) {
|
|
||||||
return SamplerCompareFunc::LessEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
return compareFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
||||||
const MG_State::GLState::ITextureObject& texture) {
|
const MG_State::GLState::ITextureObject& texture) {
|
||||||
if (!UsesBorderColor(sampler)) {
|
if (!UsesBorderColor(sampler)) {
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ private:
|
|||||||
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
||||||
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
||||||
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
||||||
static SamplerCompareFunc ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
|
|
||||||
const MG_State::GLState::ITextureObject& texture);
|
|
||||||
static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
||||||
const MG_State::GLState::ITextureObject& texture);
|
const MG_State::GLState::ITextureObject& texture);
|
||||||
// The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and
|
// The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ namespace MobileGL {
|
|||||||
Float maxLod = 1000.0f;
|
Float maxLod = 1000.0f;
|
||||||
Float lodBias = 0.0f;
|
Float lodBias = 0.0f;
|
||||||
Float maxAnisotropy = 1.0f;
|
Float maxAnisotropy = 1.0f;
|
||||||
SamplerCompareFunc compareFunc = SamplerCompareFunc::Always;
|
// GL 4.6 core table 23.18 / GLES 3.2 table 21.16: TEXTURE_COMPARE_FUNC starts at LEQUAL,
|
||||||
|
// for both sampler objects and the sampler state a texture object carries.
|
||||||
|
SamplerCompareFunc compareFunc = SamplerCompareFunc::LessEqual;
|
||||||
SamplerCompareMode compareMode = SamplerCompareMode::None;
|
SamplerCompareMode compareMode = SamplerCompareMode::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user