Skip to content

Commit

Permalink
[WebGL] Emulate glMapBuffer API in WebGL backend.
Browse files Browse the repository at this point in the history
- glMapBufferRange is supported in GLES 3 but not in WebGL 2,
  so we emulate it by manually mapping the GL buffer data to CPU memory space.
- Use <webgl/webgl2.h> header instead of <GLES/gl32.h>, since WebGL 2.0 is based on GLES 3.0;
  This also enables glGetBufferSubData(), which is supported in WebGL 2 but not in GLES 3.
- Added more assertions of unimplemented functions in GLProfile namespace for WebGL and GLES backends.
  • Loading branch information
LukasBanana committed Aug 31, 2024
1 parent 1b25809 commit 03ca533
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 10 deletions.
6 changes: 4 additions & 2 deletions sources/Renderer/OpenGL/GLESProfile/GLESProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "../GLProfile.h"
#include "../Ext/GLExtensions.h"
#include "../../../Core/Assertion.h"
#include <LLGL/RenderSystemFlags.h>
#include <cstring>

Expand Down Expand Up @@ -53,13 +54,13 @@ void GetTexParameterInternalFormat(GLenum target, GLint* params)
#ifdef GL_ES_VERSION_3_1
glGetTexLevelParameteriv(target, 0, GL_TEXTURE_INTERNAL_FORMAT, params);
#else
//TODO...
LLGL_TRAP_NOT_IMPLEMENTED(); //TODO
#endif
}

void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufsize, GLint* params)
{
//TODO
LLGL_TRAP_NOT_IMPLEMENTED(); //TODO
}

void DepthRange(GLclamp_t nearVal, GLclamp_t farVal)
Expand All @@ -74,6 +75,7 @@ void ClearDepth(GLclamp_t depth)

void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data)
{
LLGL_ASSERT_PTR(data);
if (void* srcData = glMapBufferRange(target, offset, size, GL_MAP_READ_BIT))
{
::memcpy(data, srcData, static_cast<std::size_t>(size));
Expand Down
35 changes: 35 additions & 0 deletions sources/Renderer/OpenGL/GLTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include "GLTypes.h"
#include "GLProfile.h"
#include "../../Core/Exception.h"


Expand Down Expand Up @@ -1192,6 +1193,40 @@ bool IsDepthStencilFormat(GLenum internalFormat)
}
}

GLenum BufferTargetToBindingPname(GLenum target)
{
switch (target)
{
case GL_ARRAY_BUFFER: return GL_ARRAY_BUFFER_BINDING;
case GL_COPY_READ_BUFFER: return GL_COPY_READ_BUFFER_BINDING;
case GL_COPY_WRITE_BUFFER: return GL_COPY_WRITE_BUFFER_BINDING;
case GL_ELEMENT_ARRAY_BUFFER: return GL_ELEMENT_ARRAY_BUFFER_BINDING;
case GL_PIXEL_PACK_BUFFER: return GL_PIXEL_PACK_BUFFER_BINDING;
case GL_PIXEL_UNPACK_BUFFER: return GL_PIXEL_UNPACK_BUFFER_BINDING;
case GL_TRANSFORM_FEEDBACK_BUFFER: return GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
case GL_UNIFORM_BUFFER: return GL_UNIFORM_BUFFER_BINDING;

#if GL_VERSION_4_0 || GL_ES_VERSION_3_1
case GL_DRAW_INDIRECT_BUFFER: return GL_DRAW_INDIRECT_BUFFER_BINDING;
#endif

#if GL_VERSION_4_2 || GL_ES_VERSION_3_1
case GL_ATOMIC_COUNTER_BUFFER: return GL_ATOMIC_COUNTER_BUFFER_BINDING;
#endif

#if GL_VERSION_4_3 || GL_ES_VERSION_3_1
case GL_DISPATCH_INDIRECT_BUFFER: return GL_DISPATCH_INDIRECT_BUFFER_BINDING;
case GL_SHADER_STORAGE_BUFFER: return GL_SHADER_STORAGE_BUFFER_BINDING;
#endif

#if GL_VERSION_4_4 || GL_ES_VERSION_3_2
case GL_TEXTURE_BUFFER: return GL_TEXTURE_BUFFER_BINDING;
#endif

default: return 0;
}
}


} // /namespace GLTypes

Expand Down
4 changes: 4 additions & 0 deletions sources/Renderer/OpenGL/GLTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ bool IsDepthFormat(GLenum internalFormat);
// Returns true if the specified GL internal format is a depth-and-stencil format.
bool IsDepthStencilFormat(GLenum internalFormat);

// Returns the binding parameter name for the specified buffer target,
// e.g. GL_UNIFOMR_BUFFER to GL_UNIFORM_BUFFER_BINDING, used for glGetIntegerv().
GLenum BufferTargetToBindingPname(GLenum target);


} // /namespace GLTypes

Expand Down
3 changes: 1 addition & 2 deletions sources/Renderer/OpenGL/WebGLProfile/WebGL.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
#if defined(LLGL_OS_WASM)
# define GL_GLEXT_PROTOTYPES 1
# define EGL_EGLEXT_PROTOTYPES 1
# include <GLES3/gl3.h>
# include <GLES3/gl32.h>
# include <webgl/webgl2.h>
#else
# error Unsupported platform for WebGL
#endif
Expand Down
69 changes: 63 additions & 6 deletions sources/Renderer/OpenGL/WebGLProfile/WebGLProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
*/

#include "../GLProfile.h"
#include "../GLTypes.h"
#include "../Ext/GLExtensions.h"
#include "../../../Core/Assertion.h"
#include <LLGL/RenderSystemFlags.h>
#include <LLGL/Container/DynamicArray.h>
#include <cstring>


Expand Down Expand Up @@ -50,12 +53,12 @@ GLint GetMaxViewports()

void GetTexParameterInternalFormat(GLenum target, GLint* params)
{
//TODO...
LLGL_TRAP_NOT_IMPLEMENTED(); //TODO
}

void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufsize, GLint* params)
{
//TODO
LLGL_TRAP_NOT_IMPLEMENTED(); //TODO
}

void DepthRange(GLclamp_t nearVal, GLclamp_t farVal)
Expand All @@ -70,22 +73,76 @@ void ClearDepth(GLclamp_t depth)

void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data)
{
// dummy
LLGL_ASSERT_PTR(data);
glGetBufferSubData(target, offset, size, data); // supported in WebGL 2 but not in GLES 3
}

static GLbitfield ToGLESMapBufferRangeAccess(GLenum access)
{
switch (access)
{
case GL_READ_ONLY: return GL_MAP_READ_BIT;
case GL_WRITE_ONLY: return GL_MAP_WRITE_BIT;
case GL_READ_WRITE: return GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
default: return 0;
}
}

// This emulates the glMapBuffer API from GLES3 for WebGL
static struct MapBufferContext
{
GLuint buffer = 0;
GLintptr offset = 0;
GLsizeiptr size = 0;
GLbitfield access = 0;
DynamicByteArray data;
}
g_mapBufferContext;

void* MapBuffer(GLenum target, GLenum access)
{
return nullptr; //TODO: allocate intermdiate CPU buffer
/* Translate GL access type to GLES bitfield, determine buffer length, and map entire buffer range */
GLbitfield flags = ToGLESMapBufferRangeAccess(access);
GLint length = 0;
glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);
return MapBufferRange(target, 0, length, flags);
}

void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
{
return nullptr; //TODO: allocate intermdiate CPU buffer
LLGL_ASSERT(g_mapBufferContext.access == 0, "cannot map multiple GL buffers interleaved in WebGL backend");

if (access == 0 || !(length > 0))
return nullptr;

/* Get currently bound buffer at the specified target */
GLint bufferBinding = GLTypes::BufferTargetToBindingPname(target);
GLint buffer = 0;
glGetIntegerv(bufferBinding, &buffer);

/* Allocate intermdiate buffer and store in global context */
g_mapBufferContext.buffer = static_cast<GLuint>(buffer);
g_mapBufferContext.offset = offset;
g_mapBufferContext.size = length;
g_mapBufferContext.access = access;
g_mapBufferContext.data.resize(static_cast<std::size_t>(length), UninitializeTag{});

/* Manually map GL buffer into CPU memory space to emulate glMapBuffer API for WebGL */
if ((access & GL_MAP_READ_BIT) != 0)
glGetBufferSubData(target, offset, length, g_mapBufferContext.data.get()); // supported in WebGL 2 but not in GLES 3

return g_mapBufferContext.data.get();
}

void UnmapBuffer(GLenum target)
{
//TODO: release intermediate CPU buffer
if (g_mapBufferContext.access != 0)
{
/* Write data back to buffer */
if ((g_mapBufferContext.access & GL_MAP_WRITE_BIT) != 0)
glBufferSubData(target, g_mapBufferContext.offset, g_mapBufferContext.size, g_mapBufferContext.data.get());
g_mapBufferContext.access = 0;
}
}

void DrawBuffer(GLenum buf)
Expand Down

0 comments on commit 03ca533

Please sign in to comment.