From d4beca5ad2e8a9172df238977fac551fd96a3ca5 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 13:48:33 +0100 Subject: [PATCH] sokol_gfx.h glcore: allow multisampled texture bindings --- sokol_gfx.h | 72 +++++++++++++++++++++++++++++++----------- util/sokol_gfx_imgui.h | 1 + 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index cda162e3b..ddd88e987 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -1871,6 +1871,7 @@ typedef struct sg_features { bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks bool storage_buffer; // storage buffers are supported + bool msaa_image_bindings; // if true, multisampled images can be bound as texture resources } sg_features; /* @@ -4928,6 +4929,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C + #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 + #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -7091,7 +7094,9 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ - _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) \ + _SG_XMACRO(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \ + _SG_XMACRO(glTexImage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -7143,13 +7148,22 @@ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { } } -_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { - switch (t) { - case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; - case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; - case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; - case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; - default: SOKOL_UNREACHABLE; return 0; +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t, int sample_count) { + const bool msaa = sample_count > 1; + if (msaa) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D_MULTISAMPLE; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } + } else { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } } } @@ -7816,6 +7830,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = true; _sg.features.storage_buffer = version >= 430; + _sg.features.msaa_image_bindings = true; // scan extensions bool has_s3tc = false; // BC1..BC3 @@ -7889,6 +7904,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = false; _sg.features.storage_buffer = false; + _sg.features.msaa_image_bindings = false; bool has_s3tc = false; // BC1..BC3 bool has_rgtc = false; // BC4 and BC5 @@ -8428,6 +8444,7 @@ _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); _SG_GL_CHECK_ERROR(); + const bool msaa = img->cmn.sample_count > 1; img->gl.injected = (0 != desc->gl_textures[0]); // check if texture format is support @@ -8437,14 +8454,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ } const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); - // if this is a MSAA render target, a render buffer object will be created instead of a regulat texture + // GLES3/WebGL2 doesn't have support for multisampled textures, so create a render buffer object instead + // on GLES3, if this is a MSAA render target, a render buffer object will be created instead of a regular texture // (since GLES3 has no multisampled texture objects) - if (img->cmn.render_target && (img->cmn.sample_count > 1)) { + if (!_sg.features.msaa_image_bindings && img->cmn.render_target && msaa) { glGenRenderbuffers(1, &img->gl.msaa_render_buffer); glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); } else if (img->gl.injected) { - img->gl.target = _sg_gl_texture_target(img->cmn.type); + img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count); // inject externally GL textures for (int slot = 0; slot < img->cmn.num_slots; slot++) { SOKOL_ASSERT(desc->gl_textures[slot]); @@ -8455,7 +8473,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ } } else { // create our own GL texture(s) - img->gl.target = _sg_gl_texture_target(img->cmn.type); + img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count); const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); for (int slot = 0; slot < img->cmn.num_slots; slot++) { @@ -8471,6 +8489,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ bool tex_storage_allocated = false; #if defined(__EMSCRIPTEN__) if (desc->data.subimage[0][0].ptr == 0) { + SOKOL_ASSERT(!msaa); tex_storage_allocated = true; if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { glTexStorage2D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height); @@ -8493,13 +8512,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { if (is_compressed) { + SOKOL_ASSERT(!msaa); const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + if (msaa) { + glTexImage2DMultisample(gl_img_target, img->cmn.sample_count, (GLint)gl_internal_format, + mip_width, mip_height, GL_TRUE); + } else { + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } } } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { int mip_depth = img->cmn.num_slices; @@ -8507,13 +8532,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ mip_depth = _sg_miplevel_dim(mip_depth, mip_index); } if (is_compressed) { + SOKOL_ASSERT(!msaa); const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, mip_depth, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + if (msaa) { + // NOTE: only for array textures, not actual 3D textures! + glTexImage3DMultisample(gl_img_target, img->cmn.sample_count, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, GL_TRUE); + } else { + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } } } } @@ -10478,6 +10510,7 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; _sg.features.storage_buffer = true; + _sg.features.msaa_image_bindings = true; _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -12261,6 +12294,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; _sg.features.storage_buffer = true; + _sg.features.msaa_image_bindings = true; _sg.features.image_clamp_to_border = false; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) @@ -17240,8 +17274,10 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { _SG_VALIDATE(img != 0, VALIDATE_ABND_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { _SG_VALIDATE(img->cmn.type == shd->cmn.images[i].image_type, VALIDATE_ABND_IMAGE_TYPE_MISMATCH); -// _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_IMAGE_MSAA); - if (shd->cmn.images[0].multisampled) { + if (!_sg.features.msaa_image_bindings) { + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_IMAGE_MSAA); + } + if (shd->cmn.images[i].multisampled) { _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ABND_EXPECTED_MULTISAMPLED_IMAGE); } const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; diff --git a/util/sokol_gfx_imgui.h b/util/sokol_gfx_imgui.h index 476e1471b..9873bb419 100644 --- a/util/sokol_gfx_imgui.h +++ b/util/sokol_gfx_imgui.h @@ -4212,6 +4212,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_caps_panel(void) { igText(" mrt_independent_blend_state: %s", _sgimgui_bool_string(f.mrt_independent_blend_state)); igText(" mrt_independent_write_mask: %s", _sgimgui_bool_string(f.mrt_independent_write_mask)); igText(" storage_buffer: %s", _sgimgui_bool_string(f.storage_buffer)); + igText(" msaa_image_bindings: %s", _sgimgui_bool_string(f.msaa_image_bindings)); sg_limits l = sg_query_limits(); igText("\nLimits:\n"); igText(" max_image_size_2d: %d", l.max_image_size_2d);