Pārlūkot izejas kodu

sokol_gfx.h glcore: allow multisampled texture bindings

Andre Weissflog 10 mēneši atpakaļ
vecāks
revīzija
d4beca5ad2
2 mainītis faili ar 55 papildinājumiem un 18 dzēšanām
  1. 54 18
      sokol_gfx.h
  2. 1 0
      util/sokol_gfx_imgui.h

+ 54 - 18
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];

+ 1 - 0
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);