From b4bcec65cfdd3f05b0aa07394f3569090a59f5d5 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 16 Nov 2024 16:34:29 +0100 Subject: [PATCH 1/8] sokol_gfx.h mtl: allow binding multisampled textured --- sokol_gfx.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index 9deb1763a..d4144fe1d 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -3948,6 +3948,7 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding is missing or the image handle is invalid") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IMG_EXISTS, "sg_apply_bindings: bound image no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of bound image doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_MULTISAMPLED_IMAGE, "sg_apply_bindings: expected image with sample_count > 1") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected") \ @@ -12607,7 +12608,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_ // initialize MTLTextureDescriptor with MSAA attributes _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->cmn.sample_count > 1); - mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; mtl_desc.resourceOptions = MTLResourceStorageModePrivate; mtl_desc.textureType = MTLTextureType2DMultisample; mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; @@ -17238,7 +17239,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); +// _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_IMAGE_MSAA); + if (shd->cmn.images[0].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]; switch (shd->cmn.images[i].sample_type) { case SG_IMAGESAMPLETYPE_FLOAT: From 622e746b67e80764e90b65da2b432df24da08bbd Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 12:51:18 +0100 Subject: [PATCH 2/8] sokol_gfx.h d3d11: allow binding msaa textures, don't allow msaa cubemaps in validation layer --- sokol_gfx.h | 69 +++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index d4144fe1d..cda162e3b 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -3795,6 +3795,7 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_CUBE_IMAGE, "cube images cannot have sample_count > 1") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ @@ -10625,6 +10626,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const const bool injected = (0 != desc->d3d11_texture); const bool msaa = (img->cmn.sample_count > 1); + SOKOL_ASSERT(!(msaa && (img->cmn.type == SG_IMAGETYPE_CUBE))); img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); @@ -10660,20 +10662,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const default: d3d11_tex_desc.ArraySize = 1; break; } d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; if (img->cmn.render_target) { d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { - d3d11_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + d3d11_tex_desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL; } else { - d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - } - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; } d3d11_tex_desc.CPUAccessFlags = 0; } else { d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; @@ -10688,35 +10687,32 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); // create shader-resource-view for 2D texture - // FIXME: currently we don't support setting MSAA texture as shader resource - if (!msaa) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); - switch (img->cmn.type) { - case SG_IMAGETYPE_2D: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_CUBE: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_ARRAY: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; - break; - default: - SOKOL_UNREACHABLE; break; - } - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); - return SG_RESOURCESTATE_FAILED; - } - _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = msaa ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = msaa ? D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY : D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); } SOKOL_ASSERT(img->d3d11.tex2d); img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; @@ -11542,6 +11538,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { if (vb == 0) { continue; } + SOKOL_ASSERT(vb->d3d11.buf); d3d11_vbs[i] = vb->d3d11.buf; d3d11_vb_offsets[i] = (UINT)bnd->vb_offsets[i]; } @@ -11554,6 +11551,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) || (stage == SG_SHADERSTAGE_FRAGMENT)); const uint8_t d3d11_slot = shd->d3d11.img_register_t_n[i]; SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_TEX_SBUF_BINDINGS); + SOKOL_ASSERT(img->d3d11.srv); if (stage == SG_SHADERSTAGE_VERTEX) { d3d11_vs_srvs[d3d11_slot] = img->d3d11.srv; } else { @@ -11569,6 +11567,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) || (stage == SG_SHADERSTAGE_FRAGMENT)); const uint8_t d3d11_slot = shd->d3d11.sbuf_register_t_n[i]; SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_TEX_SBUF_BINDINGS); + SOKOL_ASSERT(sbuf->d3d11.srv); if (stage == SG_SHADERSTAGE_VERTEX) { d3d11_vs_srvs[d3d11_slot] = sbuf->d3d11.srv; } else { @@ -11584,6 +11583,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) || (stage == SG_SHADERSTAGE_FRAGMENT)); const uint8_t d3d11_slot = shd->d3d11.smp_register_s_n[i]; SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SMP_BINDINGS); + SOKOL_ASSERT(smp->d3d11.smp); if (stage == SG_SHADERSTAGE_VERTEX) { d3d11_vs_smps[d3d11_slot] = smp->d3d11.smp; } else { @@ -16532,6 +16532,7 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_CUBE, VALIDATE_IMAGEDESC_MSAA_CUBE_IMAGE); } } else { _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); From d4beca5ad2e8a9172df238977fac551fd96a3ca5 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 13:48:33 +0100 Subject: [PATCH 3/8] 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); From 623d928c1bb02df6f2d67b796e2181b5253c8dfa Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 15:27:57 +0100 Subject: [PATCH 4/8] sokol_gfx.h webgl2: ifdef-out any msaa texture code --- sokol_gfx.h | 61 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index ddd88e987..3287b909e 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -7149,14 +7149,25 @@ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { } _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; + #if defined(SOKOL_GLCORE) + 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; + } } - } else { + #else + SOKOL_ASSERT(sample_count == 1); _SOKOL_UNUSED(sample_count); switch (t) { case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; @@ -7164,7 +7175,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t, int sample_count) { case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; default: SOKOL_UNREACHABLE; return 0; } - } + #endif } _SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { @@ -8518,13 +8529,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ mip_width, mip_height, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - if (msaa) { - glTexImage2DMultisample(gl_img_target, img->cmn.sample_count, (GLint)gl_internal_format, - mip_width, mip_height, GL_TRUE); - } else { + #if defined(SOKOL_GLCORE) + 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 + SOKOL_ASSERT(!msaa); glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, mip_width, mip_height, 0, gl_format, gl_type, data_ptr); - } + #endif } } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { int mip_depth = img->cmn.num_slices; @@ -8538,14 +8555,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ mip_width, mip_height, mip_depth, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - 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 { + #if defined(SOKOL_GLCORE) + 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); + } + #else + SOKOL_ASSERT(!msaa); glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); - } + #endif } } } From 15b6dde7a17ea9b8d1dc7b0cd90ee4327fa07c52 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 15:50:05 +0100 Subject: [PATCH 5/8] sokol_gfx.h wgpu: fix for msaa texture bindings --- sokol_gfx.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index 3287b909e..b0f3eb61b 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -13707,9 +13707,9 @@ _SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) } } -_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t) { +_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t, bool msaa) { switch (t) { - case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureSampleType_Float; + case SG_IMAGESAMPLETYPE_FLOAT: return msaa ? WGPUTextureSampleType_UnfilterableFloat : WGPUTextureSampleType_Float; case SG_IMAGESAMPLETYPE_DEPTH: return WGPUTextureSampleType_Depth; case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureSampleType_Sint; case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureSampleType_Uint; @@ -14021,6 +14021,7 @@ _SOKOL_PRIVATE void _sg_wgpu_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; wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); @@ -15078,13 +15079,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const if (shd->cmn.images[i].stage == SG_SHADERSTAGE_NONE) { continue; } + const bool msaa = shd->cmn.images[i].multisampled; shd->wgpu.img_grp1_bnd_n[i] = desc->images[i].wgsl_group1_binding_n; WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index]; bgl_entry->binding = shd->wgpu.img_grp1_bnd_n[i]; bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.images[i].stage); bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(shd->cmn.images[i].image_type); - bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(shd->cmn.images[i].sample_type); - bgl_entry->texture.multisampled = shd->cmn.images[i].multisampled; + bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(shd->cmn.images[i].sample_type, msaa); + bgl_entry->texture.multisampled = msaa; bgl_index += 1; } for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { From eefc84ead8ad29909ec03e9c8d849f9174f4c3e7 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 16:26:34 +0100 Subject: [PATCH 6/8] sokol_gfx.h gl: fix wrong cast in glTexImage*Multisample() calls --- sokol_gfx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index b0f3eb61b..3a4756a8c 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -8531,7 +8531,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); #if defined(SOKOL_GLCORE) if (msaa) { - glTexImage2DMultisample(gl_img_target, img->cmn.sample_count, (GLint)gl_internal_format, + glTexImage2DMultisample(gl_img_target, img->cmn.sample_count, gl_internal_format, mip_width, mip_height, GL_TRUE); } else { glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, @@ -8558,7 +8558,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ #if defined(SOKOL_GLCORE) if (msaa) { // NOTE: only for array textures, not actual 3D textures! - glTexImage3DMultisample(gl_img_target, img->cmn.sample_count, (GLint)gl_internal_format, + glTexImage3DMultisample(gl_img_target, img->cmn.sample_count, gl_internal_format, mip_width, mip_height, mip_depth, GL_TRUE); } else { glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, From 9f0f5a4a5a8b92215e237ef8f5a84121ff9abdbd Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sun, 17 Nov 2024 16:46:24 +0100 Subject: [PATCH 7/8] sokol_gfx.h gl: disable multisampled textures on macOS+GL --- sokol_gfx.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sokol_gfx.h b/sokol_gfx.h index 3a4756a8c..c66852e1c 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -7841,7 +7841,11 @@ _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; + #if defined(__APPLE__) + _sg.features.msaa_image_bindings = false; + #else _sg.features.msaa_image_bindings = true; + #endif // scan extensions bool has_s3tc = false; // BC1..BC3 @@ -8465,9 +8469,7 @@ _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); - // 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) + // GLES3/WebGL2/macOS doesn't have support for multisampled textures, so create a render buffer object instead 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); @@ -8529,7 +8531,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ mip_width, mip_height, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - #if defined(SOKOL_GLCORE) + #if defined(SOKOL_GLCORE) && !defined(__APPLE__) if (msaa) { glTexImage2DMultisample(gl_img_target, img->cmn.sample_count, gl_internal_format, mip_width, mip_height, GL_TRUE); @@ -8555,7 +8557,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ mip_width, mip_height, mip_depth, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - #if defined(SOKOL_GLCORE) + #if defined(SOKOL_GLCORE) && !defined(__APPLE__) if (msaa) { // NOTE: only for array textures, not actual 3D textures! glTexImage3DMultisample(gl_img_target, img->cmn.sample_count, gl_internal_format, From 6909c46e53f15a009c1ce13c9628830497a855f3 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Tue, 19 Nov 2024 18:34:03 +0100 Subject: [PATCH 8/8] update changelog (https://github.com/floooh/sokol/pull/1155) --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba77a1901..d39522615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ ## Updates +### 19-Nov-2024 + +- Merged PR https://github.com/floooh/sokol/pull/1155, this allows to use + MSAA textures as resource bindings to load individual MSAA samples in + shaders. This is an optional feature and isn't supported on the following + platform/backend combos: + + - macOS+GL + - iOS+GLES3 + - WebGL2 + - Android + + You can also check the new feature flag `sg_features.msaa_image_bindings` + for support at runtime. + + There's also a new sample https://floooh.github.io/sokol-webgpu/customresolve-sapp.html + which demonstrates how to access multisampled textures and the MSAA coverage mask + (requires a browser with WebGPU support). + ### 13-Nov-2024 - sokol_nuklear.h: merge PR https://github.com/floooh/sokol/pull/1150, this allows to connect the