[Feat] (MG_Impl, MG_State, MG_Util): attach a buffer texture to a range of its buffer

glTexBufferRange, glTextureBuffer and glTextureBufferRange were all stubs, so a
buffer texture could only ever be attached through glTexBuffer -- by binding, and
always to the whole buffer.

Give the buffer texture the window it is supposed to address. The non-range forms
record it as offset 0 with a whole-buffer sentinel rather than the size the buffer
happens to have, so a later respecify keeps being followed instead of freezing the
texture at yesterday's size. All four entry points now share one attach path,
differing only in how they name the texture: by binding for the target forms, by
name for the DSA ones.

Both backends honour the window: DirectVulkan offsets and clamps the buffer view,
DirectGLES uses glTexBufferRange when the texture names a sub-range and keeps
plain glTexBuffer for the whole-buffer case, which also works on a driver without
the range entry point.

GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT reported 0 with a comment explaining that the
range entry points were stubbed. It now reports what the device actually requires
-- minTexelBufferOffsetAlignment on Vulkan, the driver's own value on GLES -- and
the range entry points enforce it. Zero was never a legal answer; the minimum is
1, and an application that trusted it would have built unaligned offsets.
This commit is contained in:
BZLZHH
2026-08-04 20:53:53 -04:00
parent f39e6eb82d
commit bb582203d9
14 changed files with 155 additions and 7 deletions
+2
View File
@@ -315,6 +315,8 @@ namespace MobileGL {
Int MaxComputeWorkGroupInvocations = 128; Int MaxComputeWorkGroupInvocations = 128;
Int MaxShaderStorageBufferBindings = 8; Int MaxShaderStorageBufferBindings = 8;
Int MaxTextureBufferSize = 65536; Int MaxTextureBufferSize = 65536;
// GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT; 1 means the offset is unconstrained.
Int TextureBufferOffsetAlignment = 1;
Int MaxUniformBufferBindings = 24; Int MaxUniformBufferBindings = 24;
Int MaxUniformBlockSize = 16384; Int MaxUniformBlockSize = 16384;
Int MaxImageUnits = 8; Int MaxImageUnits = 8;
@@ -1112,6 +1112,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_dynamicParameters.MaxComputeWorkGroupInvocations = m_GLESCapabilities.MaxComputeWorkGroupInvocations; m_dynamicParameters.MaxComputeWorkGroupInvocations = m_GLESCapabilities.MaxComputeWorkGroupInvocations;
m_dynamicParameters.MaxShaderStorageBufferBindings = m_GLESCapabilities.MaxShaderStorageBufferBindings; m_dynamicParameters.MaxShaderStorageBufferBindings = m_GLESCapabilities.MaxShaderStorageBufferBindings;
m_dynamicParameters.MaxTextureBufferSize = m_GLESCapabilities.MaxTextureBufferSize; m_dynamicParameters.MaxTextureBufferSize = m_GLESCapabilities.MaxTextureBufferSize;
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 =
+18 -1
View File
@@ -2232,7 +2232,24 @@ namespace MobileGL::MG_Backend::DirectGLES {
"ID: %u, buffer ID: %u, buffer size: %zu, format: %s", "ID: %u, buffer ID: %u, buffer size: %zu, format: %s",
m_backendTextureId, backendId, buffer->GetSize(), m_backendTextureId, backendId, buffer->GetSize(),
MG_Util::ConvertGLEnumToString(glInternalFormat).c_str()); MG_Util::ConvertGLEnumToString(glInternalFormat).c_str());
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); // A texture that names a window of the buffer needs the range form; the
// whole-buffer forms report offset 0 and the buffer's current size, which
// glTexBuffer expresses more directly (and works where the range entry point
// is absent).
const SizeT rangeOffset = textureBufferObject->GetBufferRangeOffset();
const SizeT rangeSize = textureBufferObject->GetBufferRangeSizeInBytes();
if (rangeOffset == 0 && rangeSize == buffer->GetSize()) {
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
} else if (g_GLESFuncs.glTexBufferRange != nullptr) {
g_GLESFuncs.glTexBufferRange(GL_TEXTURE_BUFFER, glInternalFormat, backendId,
static_cast<GLintptr>(rangeOffset),
static_cast<GLsizeiptr>(rangeSize));
} else {
MGLOG_E("Texture buffer %u names a sub-range but the driver has no "
"glTexBufferRange; binding the whole buffer instead",
stateTextureObject->GetExternalIndex());
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
}
DebugImpl::ErrorLopper::Loop( DebugImpl::ErrorLopper::Loop(
[file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) { [file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) {
MGLOG_D("%s(%s:%d) glTexBuffer(format=%s, buffer=%u) ES error: %s", MGLOG_D("%s(%s:%d) glTexBuffer(format=%s, buffer=%u) ES error: %s",
@@ -780,6 +780,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_dynamicParameters.MaxComputeWorkGroupInvocations = m_vulkanCaps.MaxComputeWorkGroupInvocations; m_dynamicParameters.MaxComputeWorkGroupInvocations = m_vulkanCaps.MaxComputeWorkGroupInvocations;
m_dynamicParameters.MaxShaderStorageBufferBindings = m_vulkanCaps.MaxShaderStorageBufferBindings; m_dynamicParameters.MaxShaderStorageBufferBindings = m_vulkanCaps.MaxShaderStorageBufferBindings;
m_dynamicParameters.MaxTextureBufferSize = m_vulkanCaps.MaxTextureBufferSize; m_dynamicParameters.MaxTextureBufferSize = m_vulkanCaps.MaxTextureBufferSize;
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 =
@@ -600,7 +600,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkDeviceSize texelSize = const VkDeviceSize texelSize =
static_cast<VkDeviceSize>(MG_Util::GetSizedInternalFormatSizeInBytes(internalFormat)); static_cast<VkDeviceSize>(MG_Util::GetSizedInternalFormatSizeInBytes(internalFormat));
VkDeviceSize viewRange = slice.size; // glTextureBufferRange addresses a window of the buffer, not all of it; the whole-buffer
// forms report the buffer's current size here, so both go through the same clamp.
const VkDeviceSize rangeOffset = static_cast<VkDeviceSize>(textureBuffer->GetBufferRangeOffset());
const VkDeviceSize rangeSize = static_cast<VkDeviceSize>(textureBuffer->GetBufferRangeSizeInBytes());
VkDeviceSize viewRange = std::min(rangeSize, slice.size > rangeOffset ? slice.size - rangeOffset : 0);
if (texelSize > 0) { if (texelSize > 0) {
viewRange = (viewRange / texelSize) * texelSize; viewRange = (viewRange / texelSize) * texelSize;
} }
@@ -613,7 +617,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
viewInfo.buffer = slice.buffer; viewInfo.buffer = slice.buffer;
viewInfo.format = vkFormat; viewInfo.format = vkFormat;
viewInfo.offset = slice.offset; viewInfo.offset = slice.offset + rangeOffset;
viewInfo.range = viewRange; viewInfo.range = viewRange;
VkBufferView bufferView = VK_NULL_HANDLE; VkBufferView bufferView = VK_NULL_HANDLE;
@@ -434,7 +434,7 @@ DECLARE_GL_FUNCTION_HEAD(void, SamplerParameterIuiv, GLuint sampler, GLenum pnam
DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSamplerParameterIiv, sampler, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSamplerParameterIiv, sampler, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSamplerParameterIuiv, sampler, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSamplerParameterIuiv, sampler, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, TexBuffer, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexBuffer, target, internalformat, buffer) DECLARE_GL_FUNCTION_HEAD(void, TexBuffer, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexBuffer, target, internalformat, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexBufferRange, target, internalformat, buffer, offset, size) DECLARE_GL_FUNCTION_HEAD(void, TexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexBufferRange, target, internalformat, buffer, offset, size)
DECLARE_GL_FUNCTION_HEAD(void, TexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_HEAD(void, TexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations)
DECLARE_GL_FUNCTION_HEAD(void*, MapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) DECLARE_GL_FUNCTION_END(void*, MapBufferRange, target, offset, length, access) DECLARE_GL_FUNCTION_HEAD(void*, MapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) DECLARE_GL_FUNCTION_END(void*, MapBufferRange, target, offset, length, access)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearIndex, GLfloat c) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearIndex, c) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearIndex, GLfloat c) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearIndex, c)
@@ -1049,8 +1049,8 @@ DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GL
DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height) DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTextures, target, n, textures) DECLARE_GL_FUNCTION_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTextures, target, n, textures)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBuffer, GLuint texture, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBuffer, texture, internalformat, buffer) DECLARE_GL_FUNCTION_HEAD(void, TextureBuffer, GLuint texture, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureBuffer, texture, internalformat, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferRange, GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferRange, texture, internalformat, buffer, offset, size) DECLARE_GL_FUNCTION_HEAD(void, TextureBufferRange, GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureBufferRange, texture, internalformat, buffer, offset, size)
DECLARE_GL_FUNCTION_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width) DECLARE_GL_FUNCTION_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width)
DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height) DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth) DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth)
+1 -1
View File
@@ -1669,7 +1669,7 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = static_cast<GLint>(MG_State::pGLContext->GetHint(pname)); *params = static_cast<GLint>(MG_State::pGLContext->GetHint(pname));
return; return;
case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT: case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT:
*params = 0; // texture-buffer range entrypoints are stubbed *params = MG_Backend::pActiveBackendObject->GetDynamicParameters().TextureBufferOffsetAlignment;
return; return;
case GL_TIMESTAMP: { case GL_TIMESTAMP: {
Int64 timestamp = 0; Int64 timestamp = 0;
@@ -2035,6 +2035,65 @@ namespace MobileGL::MG_Impl::GLImpl {
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level); MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
} }
// The work glTexBuffer[Range] and glTextureBuffer[Range] all share once the texture has been
// resolved - by binding for the target forms, by name for the DSA ones. `size` is
// kWholeBuffer for the non-Range entry points, which attach the buffer as it grows rather
// than freezing the size it happens to have now.
static void AttachBufferToTexture(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
GLenum internalformat, GLuint buffer, GLintptr offset, SizeT size,
const char* caller) {
using MG_State::GLState::TextureObjectBuffer;
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
if (buffer != 0 && !bufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"`buffer` is not zero and is not the name of an existing buffer object."));
return;
}
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"The effective target of `texture` is not `GL_TEXTURE_BUFFER`."));
return;
}
if (size != TextureObjectBuffer::kWholeBuffer) {
// GL 4.6 core 8.9: offset must be non-negative and aligned to
// GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, and size must be positive.
if (offset < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "offset must be non-negative."));
return;
}
if (size == 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "size must be greater than zero."));
return;
}
const Int alignment = std::max(
1, MG_Backend::pActiveBackendObject->GetDynamicParameters().TextureBufferOffsetAlignment);
if (offset % alignment != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"offset is not a multiple of GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT."));
return;
}
}
auto* texBufferObject = static_cast<TextureObjectBuffer*>(textureObject.get());
texBufferObject->GetBufferBindingSlot().Bind(bufferObject);
texBufferObject->SetBufferRange(static_cast<SizeT>(offset < 0 ? 0 : offset), size);
texBufferObject->SetInternalFormat(textureInternalFormat);
}
void TexBuffer_State(GLenum target, GLenum internalformat, GLuint buffer) { void TexBuffer_State(GLenum target, GLenum internalformat, GLuint buffer) {
// ======================= Converting ================================ // ======================= Converting ================================
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
@@ -2081,6 +2140,7 @@ namespace MobileGL::MG_Impl::GLImpl {
auto& bufferSlot = texBufferObject->GetBufferBindingSlot(); auto& bufferSlot = texBufferObject->GetBufferBindingSlot();
bufferSlot.Bind(bufferObject); bufferSlot.Bind(bufferObject);
texBufferObject->SetBufferRange(0, MG_State::GLState::TextureObjectBuffer::kWholeBuffer);
texBufferObject->SetInternalFormat(textureInternalFormat); texBufferObject->SetInternalFormat(textureInternalFormat);
} }
@@ -4088,6 +4148,33 @@ namespace MobileGL::MG_Impl::GLImpl {
TexBuffer_State(target, internalformat, buffer); TexBuffer_State(target, internalformat, buffer);
} }
// The buffer texture bound to `target` on the active unit - what the non-DSA range form
// operates on. Kept separate from TexBuffer_State because that one resolves the target
// itself and attaches the whole buffer.
static const SharedPtr<MG_State::GLState::ITextureObject>& GetBoundBufferTexture(GLenum target,
const char* caller) {
TextureUploadTarget uploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
if (!TextureImpl::ValidateTextureUploadTarget(uploadTarget)) return nullTextureObject;
(void)caller;
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
return activeUnit.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)).GetBoundObject();
}
void TexBufferRange(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
AttachBufferToTexture(GetBoundBufferTexture(target, __func__), internalformat, buffer, offset,
static_cast<SizeT>(size < 0 ? 0 : size), __func__);
}
void TextureBuffer(GLuint texture, GLenum internalformat, GLuint buffer) {
AttachBufferToTexture(GetTextureObjectByName(texture, __func__), internalformat, buffer, 0,
MG_State::GLState::TextureObjectBuffer::kWholeBuffer, __func__);
}
void TextureBufferRange(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
AttachBufferToTexture(GetTextureObjectByName(texture, __func__), internalformat, buffer, offset,
static_cast<SizeT>(size < 0 ? 0 : size), __func__);
}
GLboolean IsTexture(GLuint texture) { GLboolean IsTexture(GLuint texture) {
return IsTexture_State(texture); return IsTexture_State(texture);
} }
@@ -42,6 +42,9 @@ namespace MobileGL::MG_Impl::GLImpl {
void GenerateTextureMipmap(GLuint texture); void GenerateTextureMipmap(GLuint texture);
void BindTextureUnit(GLuint unit, GLuint texture); void BindTextureUnit(GLuint unit, GLuint texture);
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels); void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
void TexBufferRange(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
void TextureBuffer(GLuint texture, GLenum internalformat, GLuint buffer);
void TextureBufferRange(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels); GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
void GetTextureParameterfv(GLuint texture, GLenum pname, GLfloat* params); void GetTextureParameterfv(GLuint texture, GLenum pname, GLfloat* params);
@@ -21,10 +21,31 @@ namespace MobileGL {
BindingSlot<BufferObject>& GetBufferBindingSlot( BindingSlot<BufferObject>& GetBufferBindingSlot(
TextureUploadTarget target = TextureUploadTarget::TextureBuffer); TextureUploadTarget target = TextureUploadTarget::TextureBuffer);
// The window of the attached buffer the texture addresses. glTexBuffer attaches
// the whole buffer, which is expressed here as an offset of 0 and a size of
// kWholeBuffer so a later respecify of the buffer keeps being followed - a stored
// size would freeze the texture at the size the buffer happened to have.
static constexpr SizeT kWholeBuffer = ~static_cast<SizeT>(0);
void SetBufferRange(SizeT offset, SizeT size) {
m_bufferRangeOffset = offset;
m_bufferRangeSize = size;
}
SizeT GetBufferRangeOffset() const { return m_bufferRangeOffset; }
// Resolved against the buffer's current size, so kWholeBuffer tracks it.
SizeT GetBufferRangeSizeInBytes() const {
const auto& buffer = m_bufferBindingSlot.GetBoundObject();
const SizeT bufferSize = buffer != nullptr ? buffer->GetSize() : 0;
if (m_bufferRangeSize == kWholeBuffer) return bufferSize;
const SizeT available = bufferSize > m_bufferRangeOffset ? bufferSize - m_bufferRangeOffset : 0;
return std::min(m_bufferRangeSize, available);
}
protected: protected:
Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override; Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override;
BindingSlot<BufferObject> m_bufferBindingSlot = BindingSlot<BufferObject>(BufferTarget::Texture); BindingSlot<BufferObject> m_bufferBindingSlot = BindingSlot<BufferObject>(BufferTarget::Texture);
SizeT m_bufferRangeOffset = 0;
SizeT m_bufferRangeSize = kWholeBuffer;
const Vector<TextureUploadTarget> m_uploadTargets{TextureUploadTarget::TextureBuffer}; const Vector<TextureUploadTarget> m_uploadTargets{TextureUploadTarget::TextureBuffer};
}; };
} // namespace GLState } // namespace GLState
@@ -1056,6 +1056,10 @@ namespace MobileGL::MG_Util::BackendLoader {
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations; caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings; caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
caps.MaxTextureBufferSize = maxTextureBufferSize; caps.MaxTextureBufferSize = maxTextureBufferSize;
GLint textureBufferOffsetAlignment = 1;
glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &textureBufferOffsetAlignment);
while (glGetError() != GL_NO_ERROR) {}
caps.TextureBufferOffsetAlignment = std::max(1, textureBufferOffsetAlignment);
caps.MaxUniformBufferBindings = maxUniformBufferBindings; caps.MaxUniformBufferBindings = maxUniformBufferBindings;
caps.MaxUniformBlockSize = maxUniformBlockSize; caps.MaxUniformBlockSize = maxUniformBlockSize;
caps.MaxImageUnits = maxImageUnits; caps.MaxImageUnits = maxImageUnits;
@@ -1117,6 +1117,8 @@ namespace MobileGL {
Int MaxComputeWorkGroupInvocations = 128; Int MaxComputeWorkGroupInvocations = 128;
Int MaxShaderStorageBufferBindings = 8; Int MaxShaderStorageBufferBindings = 8;
Int MaxTextureBufferSize = 65536; Int MaxTextureBufferSize = 65536;
// GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT; 1 means the offset is unconstrained.
Int TextureBufferOffsetAlignment = 1;
Int MaxUniformBufferBindings = 24; Int MaxUniformBufferBindings = 24;
Int MaxUniformBlockSize = 16384; Int MaxUniformBlockSize = 16384;
Int MaxImageUnits = 8; Int MaxImageUnits = 8;
@@ -177,6 +177,8 @@ namespace MobileGL::MG_Util::BackendLoader {
caps.MaxComputeWorkGroupInvocations = static_cast<Int>(p.limits.maxComputeWorkGroupInvocations); caps.MaxComputeWorkGroupInvocations = static_cast<Int>(p.limits.maxComputeWorkGroupInvocations);
caps.MaxShaderStorageBufferBindings = static_cast<Int>(p.limits.maxDescriptorSetStorageBuffers); caps.MaxShaderStorageBufferBindings = static_cast<Int>(p.limits.maxDescriptorSetStorageBuffers);
caps.MaxTextureBufferSize = static_cast<Int>(p.limits.maxTexelBufferElements); caps.MaxTextureBufferSize = static_cast<Int>(p.limits.maxTexelBufferElements);
caps.TextureBufferOffsetAlignment =
static_cast<Int>(std::max<VkDeviceSize>(1, p.limits.minTexelBufferOffsetAlignment));
caps.MaxUniformBufferBindings = static_cast<Int>(p.limits.maxDescriptorSetUniformBuffers); caps.MaxUniformBufferBindings = static_cast<Int>(p.limits.maxDescriptorSetUniformBuffers);
caps.MaxUniformBlockSize = static_cast<Int>(p.limits.maxUniformBufferRange); caps.MaxUniformBlockSize = static_cast<Int>(p.limits.maxUniformBufferRange);
caps.MaxImageUnits = static_cast<Int>(p.limits.maxPerStageDescriptorStorageImages); caps.MaxImageUnits = static_cast<Int>(p.limits.maxPerStageDescriptorStorageImages);
@@ -268,6 +270,8 @@ namespace MobileGL::MG_Util::BackendLoader {
caps.MaxComputeWorkGroupInvocations = static_cast<Int>(properties.limits.maxComputeWorkGroupInvocations); caps.MaxComputeWorkGroupInvocations = static_cast<Int>(properties.limits.maxComputeWorkGroupInvocations);
caps.MaxShaderStorageBufferBindings = static_cast<Int>(properties.limits.maxDescriptorSetStorageBuffers); caps.MaxShaderStorageBufferBindings = static_cast<Int>(properties.limits.maxDescriptorSetStorageBuffers);
caps.MaxTextureBufferSize = static_cast<Int>(properties.limits.maxTexelBufferElements); caps.MaxTextureBufferSize = static_cast<Int>(properties.limits.maxTexelBufferElements);
caps.TextureBufferOffsetAlignment =
static_cast<Int>(std::max<VkDeviceSize>(1, properties.limits.minTexelBufferOffsetAlignment));
caps.MaxUniformBufferBindings = static_cast<Int>(properties.limits.maxDescriptorSetUniformBuffers); caps.MaxUniformBufferBindings = static_cast<Int>(properties.limits.maxDescriptorSetUniformBuffers);
caps.MaxUniformBlockSize = static_cast<Int>(properties.limits.maxUniformBufferRange); caps.MaxUniformBlockSize = static_cast<Int>(properties.limits.maxUniformBufferRange);
caps.MaxImageUnits = static_cast<Int>(properties.limits.maxPerStageDescriptorStorageImages); caps.MaxImageUnits = static_cast<Int>(properties.limits.maxPerStageDescriptorStorageImages);
@@ -54,6 +54,8 @@ namespace MobileGL {
Int MaxComputeWorkGroupInvocations = 128; Int MaxComputeWorkGroupInvocations = 128;
Int MaxShaderStorageBufferBindings = 8; Int MaxShaderStorageBufferBindings = 8;
Int MaxTextureBufferSize = 65536; Int MaxTextureBufferSize = 65536;
// GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT; 1 means the offset is unconstrained.
Int TextureBufferOffsetAlignment = 1;
Int MaxUniformBufferBindings = 24; Int MaxUniformBufferBindings = 24;
Int MaxUniformBlockSize = 16384; Int MaxUniformBlockSize = 16384;
Int MaxImageUnits = 8; Int MaxImageUnits = 8;