From c6f581b5c00cb19481fa039e0dd4cc48a7d5170f Mon Sep 17 00:00:00 2001 From: Apples Date: Thu, 24 Aug 2023 10:45:01 -0500 Subject: [PATCH] Added stencil support for GLES3 --- drivers/gles3/rasterizer_scene_gles3.cpp | 88 +++++++++++++++++++++- drivers/gles3/rasterizer_scene_gles3.h | 8 ++ drivers/gles3/storage/material_storage.cpp | 30 ++++++++ drivers/gles3/storage/material_storage.h | 24 ++++++ drivers/gles3/storage/texture_storage.cpp | 48 +++++++++--- drivers/gles3/storage/texture_storage.h | 3 + 6 files changed, 189 insertions(+), 12 deletions(-) diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index c517d06c84bb..0205c37c3922 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1942,10 +1942,14 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); glColorMask(0, 0, 0, 0); glClearDepth(1.0f); - glClear(GL_DEPTH_BUFFER_BIT); + glClearStencil(0); + glStencilMask(255); + scene_state.current_stencil_write_mask = 255; + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); uint64_t spec_constant = SceneShaderGLES3::DISABLE_FOG | SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL | SceneShaderGLES3::DISABLE_LIGHTMAP | SceneShaderGLES3::DISABLE_LIGHT_OMNI | SceneShaderGLES3::DISABLE_LIGHT_SPOT; @@ -1976,11 +1980,25 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; + scene_state.current_depth_function = GLES3::SceneShaderData::DEPTH_FUNCTION_LESS_OR_EQUAL; scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS; + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 255); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(255); + scene_state.current_stencil_enabled = false; + scene_state.current_stencil_compare = GL_ALWAYS; + scene_state.current_stencil_reference = 0; + scene_state.current_stencil_compare_mask = 255; + scene_state.current_stencil_op_dpfail = GL_KEEP; + scene_state.current_stencil_op_dppass = GL_KEEP; + scene_state.current_stencil_write_mask = 255; + if (!fb_cleared) { glClearDepth(1.0f); - glClear(GL_DEPTH_BUFFER_BIT); + glClearStencil(0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } if (!keep_color) { @@ -2005,6 +2023,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); + glDisable(GL_STENCIL_TEST); + scene_state.current_stencil_enabled = false; + glDepthMask(GL_FALSE); scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; @@ -2053,6 +2074,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); + glDisable(GL_STENCIL_TEST); + scene_state.current_stencil_enabled = false; + if (!flip_y) { // Restore the default winding order. glFrontFace(GL_CCW); @@ -2204,6 +2228,66 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, scene_state.current_depth_draw = shader->depth_draw; } + if (scene_state.current_stencil_enabled != shader->stencil_enabled) { + if (shader->stencil_enabled) { + glEnable(GL_STENCIL_TEST); + scene_state.current_stencil_enabled = true; + } else { + glDisable(GL_STENCIL_TEST); + scene_state.current_stencil_enabled = false; + } + } + + if (scene_state.current_stencil_enabled) { + GLenum stencil_compare_table[GLES3::SceneShaderData::STENCIL_COMPARE_MAX] = { + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, + }; + + GLenum stencil_compare = stencil_compare_table[shader->stencil_compare]; + GLuint stencil_compare_mask = 0; + GLuint stencil_write_mask = 0; + GLenum stencil_op_dpfail = GL_KEEP; + GLenum stencil_op_dppass = GL_KEEP; + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) { + stencil_compare_mask = shader->stencil_compare_mask; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE) { + stencil_op_dppass = GL_REPLACE; + stencil_write_mask = shader->stencil_write_mask; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE_DEPTH_FAIL) { + stencil_op_dpfail = GL_REPLACE; + stencil_write_mask = shader->stencil_write_mask; + } + + if (scene_state.current_stencil_compare != stencil_compare || scene_state.current_stencil_reference != shader->stencil_reference || scene_state.current_stencil_compare_mask != shader->stencil_compare_mask) { + glStencilFunc(stencil_compare, shader->stencil_reference, stencil_compare_mask); + scene_state.current_stencil_compare = stencil_compare; + scene_state.current_stencil_reference = shader->stencil_reference; + scene_state.current_stencil_compare_mask = stencil_compare_mask; + } + + if (scene_state.current_stencil_write_mask != stencil_write_mask) { + glStencilMask(stencil_write_mask); + scene_state.current_stencil_write_mask = stencil_write_mask; + } + + if (scene_state.current_stencil_op_dpfail != stencil_op_dpfail || scene_state.current_stencil_op_dppass != stencil_op_dppass) { + glStencilOp(GL_KEEP, stencil_op_dpfail, stencil_op_dppass); + scene_state.current_stencil_op_dpfail = stencil_op_dpfail; + scene_state.current_stencil_op_dppass = stencil_op_dppass; + } + } + if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { GLES3::SceneShaderData::BlendMode desired_blend_mode; if constexpr (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 3e680c6097cc..290cc810f819 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -372,6 +372,14 @@ class RasterizerSceneGLES3 : public RendererSceneRender { GLES3::SceneShaderData::DepthFunction current_depth_function = GLES3::SceneShaderData::DEPTH_FUNCTION_LESS_OR_EQUAL; GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK; + GLenum current_stencil_compare = GL_ALWAYS; + GLuint current_stencil_compare_mask = 255; + GLuint current_stencil_write_mask = 255; + GLint current_stencil_reference = 0; + GLenum current_stencil_op_dpfail = GL_KEEP; + GLenum current_stencil_op_dppass = GL_KEEP; + bool current_stencil_enabled = false; + bool texscreen_copied = false; bool used_screen_texture = false; bool used_normal_texture = false; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 0fc90a85506e..7845464e65fc 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2843,6 +2843,12 @@ void SceneShaderData::set_code(const String &p_code) { int cull_modei = CULL_BACK; int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + uses_point_size = false; uses_alpha = false; uses_alpha_clip = false; @@ -2938,6 +2944,20 @@ void SceneShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones; actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code); @@ -2971,6 +2991,16 @@ void SceneShaderData::set_code(const String &p_code) { uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + + bool stencil_mask_uses_ref = bool(GLOBAL_GET("rendering/driver/stencil/mask_uses_ref")); + uint32_t stencil_mask = stencil_mask_uses_ref ? stencil_reference : 255; + stencil_compare_mask = stencil_mask; + stencil_write_mask = stencil_mask; + #ifdef DEBUG_ENABLED if (uses_particle_trails) { WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile rendering backends."); diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index e95455741862..047dbea1c095 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -264,6 +264,23 @@ struct SceneShaderData : public ShaderData { DEPTH_FUNCTION_MAX }; + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // not an actual operator, just the amount of operators + }; + + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + enum Cull { CULL_DISABLED, CULL_FRONT, @@ -293,6 +310,13 @@ struct SceneShaderData : public ShaderData { DepthFunction depth_function; Cull cull_mode; + StencilCompare stencil_compare; + uint32_t stencil_flags; + uint32_t stencil_compare_mask; + uint32_t stencil_write_mask; + int32_t stencil_reference; + bool stencil_enabled; + bool uses_point_size; bool uses_alpha; bool uses_blend_alpha; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index a3f230f9e2aa..96c0f0b951c8 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1722,14 +1722,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { ERR_FAIL_COND(!texture); rt->depth = texture->tex_id; + rt->depth_has_stencil = rt->overridden.depth_has_stencil; } else { glGenTextures(1, &rt->depth); glBindTexture(texture_target, rt->depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -1737,16 +1738,19 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target depth texture"); + rt->depth_has_stencil = true; + + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 4, "Render target depth texture"); } + #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -1893,12 +1897,33 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo if (rt->backbuffer_depth == 0 && uses_depth_texture) { glGenTextures(1, &rt->backbuffer_depth); glBindTexture(texture_target, rt->backbuffer_depth); + + GLint internal_format; + GLenum format; + GLenum type; + GLenum attachment; + int element_size; + + if (rt->depth_has_stencil) { + internal_format = GL_DEPTH24_STENCIL8; + format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + element_size = 4; + } else { + internal_format = GL_DEPTH_COMPONENT24; + format = GL_DEPTH_COMPONENT; + type = GL_UNSIGNED_INT; + attachment = GL_DEPTH_ATTACHMENT; + element_size = 3; + } + if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, internal_format, rt->size.x, rt->size.y, rt->view_count, 0, format, type, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, internal_format, rt->size.x, rt->size.y, 0, format, type, nullptr); } - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target backbuffer depth texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * element_size, "Render target backbuffer depth texture"); glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -1906,12 +1931,12 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, rt->backbuffer_depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, rt->backbuffer_depth, 0); } } } @@ -2083,6 +2108,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->overridden.color = p_color_texture; rt->overridden.depth = p_depth_texture; + rt->overridden.depth_has_stencil = false; rt->overridden.is_overridden = true; uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); @@ -2094,6 +2120,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->fbo = cache->get().fbo; rt->color = cache->get().color; rt->depth = cache->get().depth; + rt->depth_has_stencil = cache->get().depth_has_stencil; rt->size = cache->get().size; rt->texture = p_color_texture; return; @@ -2105,6 +2132,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color new_entry.fbo = rt->fbo; new_entry.color = rt->color; new_entry.depth = rt->depth; + new_entry.depth_has_stencil = rt->depth_has_stencil; new_entry.size = rt->size; // Keep track of any textures we had to allocate because they weren't overridden. if (p_color_texture.is_null()) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index c25dbef288d1..5122238b1ae4 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -355,6 +355,7 @@ struct RenderTarget { GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + bool depth_has_stencil = true; GLuint color_internal_format = GL_RGBA8; GLuint color_format = GL_RGBA; @@ -378,6 +379,7 @@ struct RenderTarget { struct RTOverridden { bool is_overridden = false; + bool depth_has_stencil = false; RID color; RID depth; RID velocity; @@ -388,6 +390,7 @@ struct RenderTarget { GLuint depth; Size2i size; Vector allocated_textures; + bool depth_has_stencil; }; RBMap fbo_cache; } overridden;