From 353fbcc13ddf4ba6a9a631fba211fee1508fd1af Mon Sep 17 00:00:00 2001 From: Cody Winchester Date: Mon, 9 Feb 2026 12:18:59 -0600 Subject: [PATCH] GOOENGINE: Experimental Metal Support Adds metal support for macOS users Original patch here: #114 --- .../draw/engines/gooengine/eevee_engine.cc | 2 +- .../engines/gooengine/eevee_lightprobes.cc | 4 + .../draw/engines/gooengine/eevee_materials.cc | 235 +++++++++++++++++- .../draw/engines/gooengine/eevee_private.hh | 22 +- .../gooengine/eevee_screen_raytrace.cc | 119 ++++++++- .../draw/engines/gooengine/eevee_shaders.cc | 23 +- .../engines/gooengine/eevee_shaders_extra.cc | 6 +- .../draw/engines/gooengine/eevee_shadows.cc | 59 ++++- .../gooengine/eevee_shadows_cascade.cc | 16 ++ .../engines/gooengine/eevee_shadows_cube.cc | 12 +- .../draw/engines/gooengine/eevee_volumes.cc | 13 +- .../infos/eevee_legacy_material_info.hh | 45 +++- .../engines/gooengine/shaders/lights_lib.glsl | 35 ++- .../gooengine/shaders/prepass_frag.glsl | 10 +- source/blender/gpu/GPU_material.hh | 1 + 15 files changed, 558 insertions(+), 44 deletions(-) diff --git a/source/blender/draw/engines/gooengine/eevee_engine.cc b/source/blender/draw/engines/gooengine/eevee_engine.cc index 671f9655a0e..053e92e809a 100644 --- a/source/blender/draw/engines/gooengine/eevee_engine.cc +++ b/source/blender/draw/engines/gooengine/eevee_engine.cc @@ -291,7 +291,7 @@ static void eevee_cache_finish(void *vedata) uint tot_samples = scene_eval->eevee.taa_render_samples; uint vl_samples = draw_ctx->view_layer->samples; - if (vl_samples > 0){ + if (vl_samples > 0) { tot_samples = vl_samples; } diff --git a/source/blender/draw/engines/gooengine/eevee_lightprobes.cc b/source/blender/draw/engines/gooengine/eevee_lightprobes.cc index 6c441719958..f855af0c2d9 100644 --- a/source/blender/draw/engines/gooengine/eevee_lightprobes.cc +++ b/source/blender/draw/engines/gooengine/eevee_lightprobes.cc @@ -321,6 +321,10 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat grp = DRW_shgroup_material_create(gpumat, inst->probe_background); DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); + + /* Bind all textures required by Material shaders on Metal */ + EEVEE_material_bind_resources( + grp, gpumat, sldata, vedata, nullptr, nullptr, -1.0f, false, false); } DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); diff --git a/source/blender/draw/engines/gooengine/eevee_materials.cc b/source/blender/draw/engines/gooengine/eevee_materials.cc index 2676bb82cad..b060059c74c 100644 --- a/source/blender/draw/engines/gooengine/eevee_materials.cc +++ b/source/blender/draw/engines/gooengine/eevee_materials.cc @@ -41,6 +41,17 @@ static struct { GPUTexture *util_tex; GPUTexture *noise_tex; +#ifdef __APPLE__ + /* Dummy textures for Metal. Metal requires textures to be bound and to match expected types. + * These are used as fallbacks when actual textures are not yet initialized. */ + GPUTexture *dummy_cube_array; /* For probeCubes (cube array texture) */ + GPUTexture *dummy_2d_array; /* For probePlanars, irradianceGrid, horizonBuffer (2D array) */ + GPUTexture + *dummy_uint_2d_array; /* For shadowCubeIDTexture, shadowCascadeIDTexture (UInt 2D array) */ + GPUTexture *dummy_2d; /* For refractColorBuffer (2D texture) */ + GPUTexture *dummy_3d; /* For inScattering, inTransmittance (3D textures) */ +#endif + float noise_offsets[3]; } e_data = {nullptr}; /* Engine data */ @@ -64,6 +75,28 @@ GPUTexture *EEVEE_materials_get_util_tex() return e_data.util_tex; } +#ifdef __APPLE__ +GPUTexture *EEVEE_materials_get_dummy_2d_array() +{ + return e_data.dummy_2d_array; +} + +GPUTexture *EEVEE_materials_get_dummy_cube_array() +{ + return e_data.dummy_cube_array; +} + +GPUTexture *EEVEE_materials_get_dummy_2d() +{ + return e_data.dummy_2d; +} + +GPUTexture *EEVEE_materials_get_dummy_3d() +{ + return e_data.dummy_3d; +} +#endif + void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, GPUMaterial *gpumat, EEVEE_ViewLayerData *sldata, @@ -82,8 +115,11 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, bool use_ao = GPU_material_flag_get(gpumat, GPU_MATFLAG_AO); #ifdef __APPLE__ - /* NOTE: Some implementation do not optimize out the unused samplers. */ + /* NOTE: Metal does not optimize out unused samplers, so all textures must be bound. + * Force all conditions to true to ensure complete texture binding. */ use_diffuse = use_glossy = use_refract = use_ao = true; + use_ssrefraction = true; + use_alpha_blend = true; #endif LightCache *lcache = inst->g_data->light_cache; EEVEE_EffectsInfo *effects = inst->effects; @@ -102,42 +138,186 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1); DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); if (use_diffuse || use_glossy || use_refract) { +#ifdef __APPLE__ + /* On Metal, shadow pools may be NULL during lightbake. Use fallback textures. */ + if (sldata->shadow_cube_pool != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool); + } + else { + DRW_shgroup_uniform_texture(shgrp, "shadowCubeTexture", e_data.util_tex); + } + if (sldata->shadow_cascade_pool != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); + } + else { + DRW_shgroup_uniform_texture(shgrp, "shadowCascadeTexture", e_data.util_tex); + } + if (sldata->shadow_cube_id_pool != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); + } + else { + DRW_shgroup_uniform_texture(shgrp, "shadowCubeIDTexture", e_data.dummy_uint_2d_array); + } + if (sldata->shadow_cascade_id_pool != nullptr) { + DRW_shgroup_uniform_texture_ref( + shgrp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); + } + else { + DRW_shgroup_uniform_texture(shgrp, "shadowCascadeIDTexture", e_data.dummy_uint_2d_array); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); +#endif } + if (use_diffuse || use_glossy || use_refract || use_ao) { +#ifdef __APPLE__ + if (inst->maxzbuffer != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &inst->maxzbuffer); + } + else { + DRW_shgroup_uniform_texture(shgrp, "maxzBuffer", e_data.util_tex); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &inst->maxzbuffer); +#endif } - if ((use_diffuse || use_glossy) && !use_ssrefraction) { + + if ((use_diffuse || use_glossy) +#ifndef __APPLE__ + && !use_ssrefraction +#endif + ) + { +#ifdef __APPLE__ + if (effects->gtao_horizons != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &effects->gtao_horizons); + } + else { + DRW_shgroup_uniform_texture(shgrp, "horizonBuffer", e_data.util_tex); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &effects->gtao_horizons); +#endif } + if (use_diffuse) { +#ifdef __APPLE__ + if (lcache->grid_tx.tex != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); + } + else { + DRW_shgroup_uniform_texture(shgrp, "irradianceGrid", e_data.dummy_2d_array); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); +#endif } + if (use_glossy || use_refract) { +#ifdef __APPLE__ + if (lcache->cube_tx.tex != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex); + } + else { + DRW_shgroup_uniform_texture(shgrp, "probeCubes", e_data.dummy_cube_array); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex); +#endif } + if (use_glossy) { +#ifdef __APPLE__ + if (inst->planar_pool != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &inst->planar_pool); + } + else { + DRW_shgroup_uniform_texture(shgrp, "probePlanars", e_data.dummy_2d_array); + } + if (inst->planar_depth != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "planarDepth", &inst->planar_depth); + } + else { + DRW_shgroup_uniform_texture(shgrp, "planarDepth", e_data.dummy_2d_array); + } +#else DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &inst->planar_pool); + DRW_shgroup_uniform_texture_ref(shgrp, "planarDepth", &inst->planar_depth); +#endif DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", ssr_id ? *ssr_id : 0); } + else { DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", 1); } + if (use_refract) { DRW_shgroup_uniform_float_copy( shgrp, "refractionDepth", (refract_depth) ? *refract_depth : 0.0); if (use_ssrefraction) { +#ifdef __APPLE__ + if (inst->filtered_radiance != nullptr) { + DRW_shgroup_uniform_texture_ref( + shgrp, "refractColorBuffer", &inst->filtered_radiance); + } + else { + DRW_shgroup_uniform_texture(shgrp, "refractColorBuffer", e_data.dummy_2d); + } +#else DRW_shgroup_uniform_texture_ref( shgrp, "refractColorBuffer", &inst->filtered_radiance); +#endif } } - if (use_alpha_blend) { - DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &effects->volume_scatter); - DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &effects->volume_transmit); + +#ifdef __APPLE__ + /* Metal: ALL surface shaders include volumetric_lib and require inScattering/inTransmittance. + * Always bind actual texture or fallback dummy - never NULL pointer reference. */ + { + /* Ensure dummy_3d exists for fallback. */ + if (!e_data.dummy_3d) { + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; + e_data.dummy_3d = DRW_texture_create_3d_ex(1, 1, 1, GPU_RGBA8, usage, DRW_TEX_WRAP, nullptr); + } + + GPUTexture *scatter_tex = effects->volume_scatter; + GPUTexture *transmit_tex = effects->volume_transmit; + + /* Use dummy if actual is NULL. */ + if (!scatter_tex) { + GPUTexture *vol_dummy = EEVEE_volumes_get_dummy_scatter(); + scatter_tex = vol_dummy ? vol_dummy : e_data.dummy_3d; + } + if (!transmit_tex) { + GPUTexture *vol_dummy = EEVEE_volumes_get_dummy_transmit(); + transmit_tex = vol_dummy ? vol_dummy : e_data.dummy_3d; + } + + DRW_shgroup_uniform_texture(shgrp, "inScattering", scatter_tex); + DRW_shgroup_uniform_texture(shgrp, "inTransmittance", transmit_tex); } +#else + if (use_alpha_blend) { + if (effects->volume_scatter != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &effects->volume_scatter); + } + else { + GPUTexture *dummy = EEVEE_volumes_get_dummy_scatter(); + DRW_shgroup_uniform_texture(shgrp, "inScattering", dummy ? dummy : e_data.util_tex); + } + if (effects->volume_transmit != nullptr) { + DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &effects->volume_transmit); + } + else { + GPUTexture *dummy = EEVEE_volumes_get_dummy_transmit(); + DRW_shgroup_uniform_texture(shgrp, "inTransmittance", dummy ? dummy : e_data.util_tex); + } + } +#endif } static void eevee_init_noise_texture() @@ -249,6 +429,30 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, eevee_init_util_texture(); eevee_init_noise_texture(); + +#ifdef __APPLE__ + /* Create dummy textures with correct types for Metal fallbacks. + * Metal requires all textures to be bound and to match the expected type. */ + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; + if (!e_data.dummy_2d_array) { + e_data.dummy_2d_array = DRW_texture_create_2d_array_ex( + 1, 1, 1, GPU_RGBA8, usage, DRW_TEX_FILTER, nullptr); + } + if (!e_data.dummy_uint_2d_array) { + e_data.dummy_uint_2d_array = DRW_texture_create_2d_array_ex( + 1, 1, 1, GPU_RGBA8UI, usage, DRW_TEX_FILTER, nullptr); + } + if (!e_data.dummy_cube_array) { + e_data.dummy_cube_array = DRW_texture_create_cube_array_ex( + 1, 1, GPU_RGBA8, usage, DRW_TEX_FILTER, nullptr); + } + if (!e_data.dummy_2d) { + e_data.dummy_2d = DRW_texture_create_2d_ex(1, 1, GPU_RGBA8, usage, DRW_TEX_FILTER, nullptr); + } + if (!e_data.dummy_3d) { + e_data.dummy_3d = DRW_texture_create_3d_ex(1, 1, 1, GPU_RGBA8, usage, DRW_TEX_WRAP, nullptr); + } +#endif } if (draw_ctx->rv3d) { @@ -418,6 +622,25 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex); DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); +#ifdef __APPLE__ + /* Metal requires all textures to be bound. */ + DRW_shgroup_uniform_texture_ref(grp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); + DRW_shgroup_uniform_texture_ref( + grp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); + + GPUTexture *scatter_tex = stl->effects->volume_scatter; + GPUTexture *transmit_tex = stl->effects->volume_transmit; + if (!scatter_tex) { + GPUTexture *dummy = EEVEE_volumes_get_dummy_scatter(); + scatter_tex = dummy ? dummy : e_data.dummy_3d; + } + if (!transmit_tex) { + GPUTexture *dummy = EEVEE_volumes_get_dummy_transmit(); + transmit_tex = dummy ? dummy : e_data.dummy_3d; + } + DRW_shgroup_uniform_texture(grp, "inScattering", scatter_tex); + DRW_shgroup_uniform_texture(grp, "inTransmittance", transmit_tex); +#endif DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &inst->planar_pool); DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &inst->g_data->light_cache->cube_tx.tex); DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &inst->g_data->light_cache->grid_tx.tex); @@ -513,7 +736,7 @@ BLI_INLINE void material_shadow(EEVEE_Data *vedata, ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED); float alpha_clip_threshold = (ma->blend_shadow == MA_BS_CLIP) ? ma->alpha_threshold : -1.0f; - int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; + int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH | VAR_MAT_SHADOW; SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH); SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); GPUMaterial *gpumat = (use_shadow_shader) ? diff --git a/source/blender/draw/engines/gooengine/eevee_private.hh b/source/blender/draw/engines/gooengine/eevee_private.hh index 7655a7ad9bd..b8582c684ea 100644 --- a/source/blender/draw/engines/gooengine/eevee_private.hh +++ b/source/blender/draw/engines/gooengine/eevee_private.hh @@ -149,6 +149,7 @@ enum { VAR_WORLD_VOLUME = (1 << 12), VAR_MAT_SHADOW_ID = (1 << 13), VAR_DEFAULT = (1 << 14), + VAR_MAT_SHADOW = (1 << 15), }; /* Material shader cache keys */ @@ -1092,6 +1093,12 @@ void eevee_id_update(void *vedata, ID *id); /* `eevee_materials.cc` */ GPUTexture *EEVEE_materials_get_util_tex(); /* XXX */ +#ifdef __APPLE__ +GPUTexture *EEVEE_materials_get_dummy_2d_array(); +GPUTexture *EEVEE_materials_get_dummy_cube_array(); +GPUTexture *EEVEE_materials_get_dummy_3d(); +#endif + void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); @@ -1544,6 +1551,8 @@ void EEVEE_volumes_resolve(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_volumes_free(); +GPUTexture *EEVEE_volumes_get_dummy_scatter(); +GPUTexture *EEVEE_volumes_get_dummy_transmit(); /* `eevee_effects.cc` */ @@ -1606,13 +1615,24 @@ float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth); /* Shadow Matrix */ static const float texcomat[4][4] = { - /* From NDC to TexCo */ + /* From NDC to TexCo (OpenGL: NDC Z is [-1,1]) */ {0.5f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.5f, 0.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, }; +#ifdef __APPLE__ +/* Metal: When using Metal-adjusted projmat (Z already in [0,1]), texcomat should NOT + * apply Z scale/offset, otherwise it's double-converted. */ +static const float texcomat_metal[4][4] = { + {0.5f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.5f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, /* Z: identity */ + {0.5f, 0.5f, 0.0f, 1.0f}, /* Z offset: 0 */ +}; +#endif + /* Cube-map Matrices */ static const float cubefacemat[6][4][4] = { /* Pos X */ diff --git a/source/blender/draw/engines/gooengine/eevee_screen_raytrace.cc b/source/blender/draw/engines/gooengine/eevee_screen_raytrace.cc index b2b3b0ba370..ed41734ab62 100644 --- a/source/blender/draw/engines/gooengine/eevee_screen_raytrace.cc +++ b/source/blender/draw/engines/gooengine/eevee_screen_raytrace.cc @@ -141,7 +141,49 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &inst->maxzbuffer); +#ifdef __APPLE__ + /* planarDepth: Use dummy 2D array if not available */ + if (inst->planar_depth != nullptr) { + DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &inst->planar_depth); + } + else { + DRW_shgroup_uniform_texture(grp, "planarDepth", EEVEE_materials_get_dummy_2d_array()); + } + /* probeCubes: Must be cube array, use dummy if not correct type */ + if (lcache->cube_tx.tex != nullptr && GPU_texture_is_cube(lcache->cube_tx.tex)) { + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); + } + else { + DRW_shgroup_uniform_texture(grp, "probeCubes", EEVEE_materials_get_dummy_cube_array()); + } + /* irradianceGrid: Must be 2D array, use dummy if wrong type */ + if (lcache->grid_tx.tex != nullptr && !GPU_texture_is_cube(lcache->grid_tx.tex)) { + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); + } + else { + DRW_shgroup_uniform_texture(grp, "irradianceGrid", EEVEE_materials_get_dummy_2d_array()); + } + /* horizonBuffer: Must be 2D texture, use util_tex as fallback */ + if (effects->gtao_horizons != nullptr && !GPU_texture_is_array(effects->gtao_horizons)) { + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); + } + else { + DRW_shgroup_uniform_texture(grp, "horizonBuffer", EEVEE_materials_get_util_tex()); + } + /* probePlanars: 2D array fallback */ + if (inst->planar_pool != nullptr) { + DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &inst->planar_pool); + } + else { + DRW_shgroup_uniform_texture(grp, "probePlanars", EEVEE_materials_get_dummy_2d_array()); + } +#else DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &inst->planar_depth); + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); + DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &inst->planar_pool); +#endif DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); @@ -172,19 +214,55 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v grp = DRW_shgroup_create(resolve_shader_refl, inst->ssr_resolve_refl); } - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); + DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); + DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); +#ifdef __APPLE__ + /* probeCubes: Check if texture is actually a cube array (not 2D array fallback). + * Metal may fail to create cube array textures and fall back to 2D array. */ + if (lcache->cube_tx.tex != nullptr && GPU_texture_is_cube(lcache->cube_tx.tex)) { DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); + } + else { + DRW_shgroup_uniform_texture(grp, "probeCubes", EEVEE_materials_get_dummy_cube_array()); + } + if (inst->planar_pool != nullptr) { DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &inst->planar_pool); + } + else { + DRW_shgroup_uniform_texture(grp, "probePlanars", EEVEE_materials_get_dummy_2d_array()); + } + if (inst->planar_depth != nullptr) { DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &inst->planar_depth); - DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter); - DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &inst->filtered_radiance); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &inst->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); + } + else { + DRW_shgroup_uniform_texture(grp, "planarDepth", EEVEE_materials_get_dummy_2d_array()); + } +#else + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); + DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &inst->planar_pool); + DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &inst->planar_depth); +#endif + DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter); + DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &inst->filtered_radiance); + DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &inst->maxzbuffer); + DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); + DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); + DRW_shgroup_uniform_texture_ref(grp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); + DRW_shgroup_uniform_texture_ref( + grp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); +#ifdef __APPLE__ + /* irradianceGrid: Check if texture is 2D array (not cube array). + * On Metal, we need to verify it's the correct type. */ + if (lcache->grid_tx.tex != nullptr && !GPU_texture_is_cube(lcache->grid_tx.tex)) { + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); + } + else { + DRW_shgroup_uniform_texture(grp, "irradianceGrid", EEVEE_materials_get_dummy_2d_array()); + } +#else + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); +#endif DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); @@ -194,7 +272,16 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_uniform_int(grp, "samplePoolOffset", &effects->taa_current_sample, 1); +#ifdef __APPLE__ + if (effects->gtao_horizons != nullptr) { + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); + } + else { + DRW_shgroup_uniform_texture(grp, "horizonBuffer", EEVEE_materials_get_util_tex()); + } +#else DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); +#endif DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); } } @@ -225,7 +312,19 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_uniform_int(grp, "samplePoolOffset", &effects->taa_current_sample, 1); +#ifdef __APPLE__ + /* horizonBuffer: Expects 2D texture. Use util_tex as fallback which is always 2D. */ + if (effects->gtao_horizons != nullptr && !GPU_texture_is_array(effects->gtao_horizons)) { + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); + } + else { + /* Use util_tex as it's a 2D texture (correct type for horizonBuffer) */ + DRW_shgroup_uniform_texture(grp, "horizonBuffer", EEVEE_materials_get_util_tex()); + } +#else DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); +#endif + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); } } diff --git a/source/blender/draw/engines/gooengine/eevee_shaders.cc b/source/blender/draw/engines/gooengine/eevee_shaders.cc index 4fcd7437e90..8a35138fde3 100644 --- a/source/blender/draw/engines/gooengine/eevee_shaders.cc +++ b/source/blender/draw/engines/gooengine/eevee_shaders.cc @@ -1259,28 +1259,36 @@ static const char *eevee_get_frag_info(int options, char **r_src) /* -- PREPASS FRAG - * Select create info permutation for `prepass_frag`. */ + const bool is_shadow = (options & VAR_MAT_SHADOW) != 0; + if (is_alpha_hash) { /* Alpha hash material variants. */ if (is_hair) { - info_name = "eevee_legacy_material_prepass_frag_alpha_hash_hair"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_alpha_hash_hair" : + "eevee_legacy_material_prepass_frag_alpha_hash_hair"; } else if (is_point_cloud) { - info_name = "eevee_legacy_material_prepass_frag_alpha_hash_pointcloud"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_alpha_hash_pointcloud" : + "eevee_legacy_material_prepass_frag_alpha_hash_pointcloud"; } else { - info_name = "eevee_legacy_material_prepass_frag_alpha_hash"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_alpha_hash" : + "eevee_legacy_material_prepass_frag_alpha_hash"; } } else { /* Opaque material variants. */ if (is_hair) { - info_name = "eevee_legacy_material_prepass_frag_opaque_hair"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_opaque_hair" : + "eevee_legacy_material_prepass_frag_opaque_hair"; } else if (is_point_cloud) { - info_name = "eevee_legacy_material_prepass_frag_opaque_pointcloud"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_opaque_pointcloud" : + "eevee_legacy_material_prepass_frag_opaque_pointcloud"; } else { - info_name = "eevee_legacy_material_prepass_frag_opaque"; + info_name = is_shadow ? "eevee_legacy_material_shadow_frag_opaque" : + "eevee_legacy_material_prepass_frag_opaque"; } } *r_src = BLI_strdup(e_data.surface_prepass_frag); @@ -1346,6 +1354,9 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_SHADOW_ID) != 0) { BLI_dynstr_append(ds, "#define USE_SHADOW_ID\n"); } + if ((options & VAR_MAT_SHADOW) != 0) { + BLI_dynstr_append(ds, "#define SHADOW_PASS\n"); + } str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); diff --git a/source/blender/draw/engines/gooengine/eevee_shaders_extra.cc b/source/blender/draw/engines/gooengine/eevee_shaders_extra.cc index 2aa5c0305ba..4fcc468051b 100644 --- a/source/blender/draw/engines/gooengine/eevee_shaders_extra.cc +++ b/source/blender/draw/engines/gooengine/eevee_shaders_extra.cc @@ -74,9 +74,13 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, } /* GooEngine: Set Depth node can write to gl_FragDepth arbitrarily. + * Only enable depth_write for materials that actually use Set Depth node + * to avoid black speckle artifacts on Metal. * This shouldn't incur a performance penalty according to * https://registry.khronos.org/OpenGL/extensions/ARB/ARB_conservative_depth.txt */ - info.depth_write(DepthWrite::ANY); + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SET_DEPTH)) { + info.depth_write(DepthWrite::ANY); + } /* Lookdev - Add FragDepth. */ if (options & VAR_MAT_LOOKDEV) { diff --git a/source/blender/draw/engines/gooengine/eevee_shadows.cc b/source/blender/draw/engines/gooengine/eevee_shadows.cc index cd1fe96dd94..5569e5e5f73 100644 --- a/source/blender/draw/engines/gooengine/eevee_shadows.cc +++ b/source/blender/draw/engines/gooengine/eevee_shadows.cc @@ -57,9 +57,13 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) int sh_cube_size = scene_eval->eevee.shadow_cube_size; int sh_cascade_size = scene_eval->eevee.shadow_cascade_size; const bool sh_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) != 0; - const bool sh_id_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_ID_HIGH_BITDEPTH) != 0; + const bool sh_id_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_ID_HIGH_BITDEPTH) != + 0; sldata->lights->soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0; + CLAMP(sh_cube_size, 256, 4096); + CLAMP(sh_cascade_size, 256, 4096); + EEVEE_LightsInfo *linfo = sldata->lights; if ((linfo->shadow_cube_size != sh_cube_size) || (linfo->shadow_high_bitdepth != sh_high_bitdepth) || @@ -68,7 +72,6 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) BLI_assert((sh_cube_size > 0) && (sh_cube_size <= 4096)); DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool); DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_id_pool); - CLAMP(sh_cube_size, 1, 4096); } if ((linfo->shadow_cascade_size != sh_cascade_size) || @@ -78,7 +81,6 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) BLI_assert((sh_cascade_size > 0) && (sh_cascade_size <= 4096)); DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool); DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_id_pool); - CLAMP(sh_cascade_size, 1, 4096); } linfo->shadow_high_bitdepth = sh_high_bitdepth; @@ -108,7 +110,8 @@ void EEVEE_shadows_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) INIT_MINMAX(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max); { - DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_SHADOW_OFFSET | DRW_STATE_WRITE_COLOR; + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_SHADOW_OFFSET | + DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(inst->shadow_pass, state); inst->g_data->shadow_shgrp = DRW_shgroup_create(EEVEE_shaders_shadow_sh_get(), @@ -211,7 +214,8 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) eGPUTextureFormat shadow_pool_format = (linfo->shadow_high_bitdepth) ? GPU_DEPTH_COMPONENT24 : GPU_DEPTH_COMPONENT16; - eGPUTextureFormat shadow_id_pool_format = (linfo->shadow_id_high_bitdepth) ? GPU_R32UI : GPU_R16UI; + eGPUTextureFormat shadow_id_pool_format = (linfo->shadow_id_high_bitdepth) ? GPU_R32UI : + GPU_R16UI; /* Setup enough layers. */ /* Free textures if number mismatch. */ @@ -246,6 +250,17 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) shadow_id_pool_format, static_cast(0), nullptr); + + /* GooEngine Fix: Manually clear all layers to 1.0 (Far) to prevent black artifacts. */ + GPUFrameBuffer *tmp_fb = GPU_framebuffer_create("shadow_clear_fb"); + int layers = max_ii(1, linfo->num_cube_layer * 6); + for (int i = 0; i < layers; i++) { + GPU_framebuffer_texture_layer_attach(tmp_fb, sldata->shadow_cube_pool, 0, i, 0); + GPU_framebuffer_bind(tmp_fb); + GPU_framebuffer_clear_depth(tmp_fb, 1.0f); + } + GPU_framebuffer_free(tmp_fb); + GPU_framebuffer_restore(); } if (!sldata->shadow_cascade_pool) { @@ -257,12 +272,24 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) shadow_usage, DRWTextureFlag(DRW_TEX_FILTER | DRW_TEX_COMPARE), nullptr); - sldata->shadow_cascade_id_pool = DRW_texture_create_2d_array(linfo->shadow_cascade_size, - linfo->shadow_cascade_size, - max_ii(1, linfo->num_cascade_layer), - shadow_id_pool_format, - static_cast(0), - nullptr); + sldata->shadow_cascade_id_pool = DRW_texture_create_2d_array( + linfo->shadow_cascade_size, + linfo->shadow_cascade_size, + max_ii(1, linfo->num_cascade_layer), + shadow_id_pool_format, + static_cast(0), + nullptr); + + /* GooEngine Fix: Manually clear all layers to 1.0 (Far) to prevent black artifacts. */ + GPUFrameBuffer *tmp_fb = GPU_framebuffer_create("shadow_clear_fb"); + int layers = max_ii(1, linfo->num_cascade_layer); + for (int i = 0; i < layers; i++) { + GPU_framebuffer_texture_layer_attach(tmp_fb, sldata->shadow_cascade_pool, 0, i, 0); + GPU_framebuffer_bind(tmp_fb); + GPU_framebuffer_clear_depth(tmp_fb, 1.0f); + } + GPU_framebuffer_free(tmp_fb); + GPU_framebuffer_restore(); } if (sldata->shadow_fb == nullptr) { @@ -408,16 +435,24 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata, void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData * /*sldata*/, EEVEE_Data *vedata) { GOOENGINE_Instance *inst = vedata->instance; - EEVEE_EffectsInfo *effects = inst->effects; if (inst->shadow_accum_fb != nullptr) { GPU_framebuffer_bind(inst->shadow_accum_fb); /* Clear texture. */ +#ifdef __APPLE__ + /* Metal: Always clear shadow accumulation buffer to prevent stale data + * when switching between viewports/workspaces. This fixes issue where + * shadow intensity changes unexpectedly due to TAA not resetting properly. */ + const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear); +#else + EEVEE_EffectsInfo *effects = inst->effects; if (effects->taa_current_sample == 1) { const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GPU_framebuffer_clear_color(inst->shadow_accum_fb, clear); } +#endif DRW_draw_pass(inst->shadow_accum_pass); diff --git a/source/blender/draw/engines/gooengine/eevee_shadows_cascade.cc b/source/blender/draw/engines/gooengine/eevee_shadows_cascade.cc index df8ee1faf8c..c054b8ab572 100644 --- a/source/blender/draw/engines/gooengine/eevee_shadows_cascade.cc +++ b/source/blender/draw/engines/gooengine/eevee_shadows_cascade.cc @@ -354,9 +354,25 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, add_v2_v2(projmat[3], jitter_ofs); } +#ifdef __APPLE__ + /* Metal: Adjust projection matrix for depth range [0, 1] instead of [-1, 1]. + * z_metal = (z_opengl + 1) / 2 = z_opengl * 0.5 + 0.5 + * CRITICAL: This must come BEFORE shadowmat calculation so that both + * shadow map rendering and shadow sampling use the same depth range. */ + projmat[2][2] *= 0.5f; + projmat[3][2] = projmat[3][2] * 0.5f + 0.5f; +#endif + + /* Calculate shadow matrix for sampling. */ float viewprojmat[4][4]; mul_m4_m4m4(viewprojmat, projmat, viewmat); +#ifdef __APPLE__ + /* Metal: Use texcomat_metal which doesn't apply Z transform since + * projmat already produces Z in [0, 1] range after above adjustment. */ + mul_m4_m4m4(csm_data->shadowmat[c], texcomat_metal, viewprojmat); +#else mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat); +#endif #ifdef DEBUG_CSM DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col); diff --git a/source/blender/draw/engines/gooengine/eevee_shadows_cube.cc b/source/blender/draw/engines/gooengine/eevee_shadows_cube.cc index 8b1d0df5879..aec0ff83cb2 100644 --- a/source/blender/draw/engines/gooengine/eevee_shadows_cube.cc +++ b/source/blender/draw/engines/gooengine/eevee_shadows_cube.cc @@ -140,6 +140,15 @@ static void eevee_ensure_cube_views( perspective_m4(winmat, -side, side, -side, side, near, far); +#ifdef __APPLE__ + /* Metal: Adjust projection matrix for depth range [0, 1] instead of [-1, 1]. + * z_metal = (z_opengl + 1) / 2 = z_opengl * 0.5 + 0.5 + * For perspective matrix: z_clip = winmat[2][2] * z + winmat[3][2] + * After Metal adjustment: z_clip_metal = z_clip * 0.5 + 0.5 */ + winmat[2][2] *= 0.5f; + winmat[3][2] = winmat[3][2] * 0.5f + 0.5f; +#endif + for (int i = 0; i < 6; i++) { float tmp[4][4]; mul_m4_m4m4(tmp, cubefacemat[i], viewmat); @@ -205,7 +214,8 @@ void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRW_view_set_active(g_data->cube_views[j]); int layer = cube_index * 6 + j; GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_pool, 0, layer, 0); - GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_id_pool, 1, layer, 0); + GPU_framebuffer_texture_layer_attach( + sldata->shadow_fb, sldata->shadow_cube_id_pool, 1, layer, 0); GPU_framebuffer_bind(sldata->shadow_fb); GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f); DRW_draw_pass(inst->shadow_pass); diff --git a/source/blender/draw/engines/gooengine/eevee_volumes.cc b/source/blender/draw/engines/gooengine/eevee_volumes.cc index 2633d6e52da..866e56f5206 100644 --- a/source/blender/draw/engines/gooengine/eevee_volumes.cc +++ b/source/blender/draw/engines/gooengine/eevee_volumes.cc @@ -369,7 +369,8 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); DRW_shgroup_uniform_texture_ref(grp, "shadowCubeIDTexture", &sldata->shadow_cube_id_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); + DRW_shgroup_uniform_texture_ref( + grp, "shadowCascadeIDTexture", &sldata->shadow_cascade_id_pool); DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &inst->volume_prop_scattering); DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &inst->volume_prop_extinction); DRW_shgroup_uniform_texture_ref(grp, "volumeEmission", &inst->volume_prop_emission); @@ -598,6 +599,16 @@ void EEVEE_volumes_free() DRW_TEXTURE_FREE_SAFE(e_data.dummy_flame); } +GPUTexture *EEVEE_volumes_get_dummy_scatter() +{ + return e_data.dummy_scatter; +} + +GPUTexture *EEVEE_volumes_get_dummy_transmit() +{ + return e_data.dummy_transmit; +} + /* -------------------------------------------------------------------- */ /** \name Render Passes * \{ */ diff --git a/source/blender/draw/engines/gooengine/shaders/infos/eevee_legacy_material_info.hh b/source/blender/draw/engines/gooengine/shaders/infos/eevee_legacy_material_info.hh index f0bb4e59707..99f9802284f 100644 --- a/source/blender/draw/engines/gooengine/shaders/infos/eevee_legacy_material_info.hh +++ b/source/blender/draw/engines/gooengine/shaders/infos/eevee_legacy_material_info.hh @@ -150,7 +150,7 @@ GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_common) ADDITIONAL_INFO(eevee_legacy_common_lib) ADDITIONAL_INFO(eevee_legacy_common_utiltex_lib) -FRAGMENT_OUT(1, UINT, resource_id_out) +/* FRAGMENT_OUT(1, UINT, resource_id_out) */ ADDITIONAL_INFO(draw_view) ADDITIONAL_INFO(eevee_legacy_closure_eval_surface_lib) GPU_SHADER_CREATE_END() @@ -163,17 +163,20 @@ GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque) ADDITIONAL_INFO(eevee_legacy_surface_lib_common) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque_hair) ADDITIONAL_INFO(eevee_legacy_surface_lib_hair) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) ADDITIONAL_INFO(draw_hair) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque_pointcloud) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) ADDITIONAL_INFO(draw_pointcloud) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() /* Common info for all `prepass_frag_alpha_hash` variants. */ @@ -186,18 +189,58 @@ GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash) ADDITIONAL_INFO(eevee_legacy_surface_lib_common) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash_hair) ADDITIONAL_INFO(eevee_legacy_surface_lib_hair) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) ADDITIONAL_INFO(draw_hair) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash_pointcloud) ADDITIONAL_INFO(eevee_legacy_surface_lib_pointcloud) ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) ADDITIONAL_INFO(draw_pointcloud) +FRAGMENT_OUT(1, VEC2, out_normal); +GPU_SHADER_CREATE_END() + +/* Shadow Variants (Same as prepass but NO Normal Output) */ + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_opaque) +ADDITIONAL_INFO(eevee_legacy_surface_lib_common) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) +GPU_SHADER_CREATE_END() + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_opaque_hair) +ADDITIONAL_INFO(eevee_legacy_surface_lib_hair) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) +ADDITIONAL_INFO(draw_hair) +GPU_SHADER_CREATE_END() + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_opaque_pointcloud) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_opaque_common) +ADDITIONAL_INFO(draw_pointcloud) +GPU_SHADER_CREATE_END() + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_alpha_hash) +ADDITIONAL_INFO(eevee_legacy_surface_lib_common) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) +GPU_SHADER_CREATE_END() + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_alpha_hash_hair) +ADDITIONAL_INFO(eevee_legacy_surface_lib_hair) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) +ADDITIONAL_INFO(draw_hair) +GPU_SHADER_CREATE_END() + + +GPU_SHADER_CREATE_INFO(eevee_legacy_material_shadow_frag_alpha_hash_pointcloud) +ADDITIONAL_INFO(eevee_legacy_surface_lib_pointcloud) +ADDITIONAL_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common) +ADDITIONAL_INFO(draw_pointcloud) +FRAGMENT_OUT(1, VEC2, out_normal); GPU_SHADER_CREATE_END() /** \} */ diff --git a/source/blender/draw/engines/gooengine/shaders/lights_lib.glsl b/source/blender/draw/engines/gooengine/shaders/lights_lib.glsl index acfd1fc7ff2..6fe2f8bb0b0 100644 --- a/source/blender/draw/engines/gooengine/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/gooengine/shaders/lights_lib.glsl @@ -182,7 +182,7 @@ float sample_ID_texture(usampler2DArray TEX_ID, vec3 coord, bool match) ivec3 tex_size = textureSize(TEX_ID, 0); // WHY THE FLYING FUCK DO WE NEED AN EXTRA 0.00195? - vec2 fra = fract((coord.xy * tex_size.xy) + vec2(0.50195, 0.50195)); + vec2 fra = fract((coord.xy * vec2(tex_size.xy)) + vec2(0.50195, 0.50195)); return mix( mix(matches.w, matches.z, fra.x), @@ -203,6 +203,16 @@ float sample_cube_shadow(int shadow_id, vec3 P, bool match_shadow_id) vec3 cubevec = transform_point(scube(data_id).shadowmat, P); float dist = max(sd(shadow_id).sh_near, max_v3(abs(cubevec)) - sd(shadow_id).sh_bias); dist = buffer_depth(true, dist, sd(shadow_id).sh_far, sd(shadow_id).sh_near); + + /* Metal: buffer_depth outputs OpenGL depth range. + * Adjust to Metal depth range [0, 1] by transforming from [-1, 1] -> [0, 1]. + * GPU projection matrices should already handle this, but buffer_depth uses + * raw perspective formula. */ +#ifdef GPU_METAL + /* Clamp to [0, 1] to prevent comparison issues */ + dist = clamp(dist, 0.0, 1.0); +#endif + /* Manual Shadow Cube Layer indexing. */ /* TODO: Shadow Cube Array. */ float face = cubeFaceIndexEEVEE(cubevec); @@ -217,6 +227,8 @@ float sample_cube_shadow(int shadow_id, vec3 P, bool match_shadow_id) return texture(shadowCubeTexture, coord_f); #endif } +/* Uncomment to debug shadow depth values - will show depth as grayscale */ + float sample_cascade_shadow(int shadow_id, vec3 P, bool match_shadow_id) { @@ -232,9 +244,19 @@ float sample_cascade_shadow(int shadow_id, vec3 P, bool match_shadow_id) float blend = fract(tot_weight); float vis = weights.w; vec4 coord, shpos; + float compare_z; + /* Main cascade. */ shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0); - coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias); + compare_z = shpos.z - sd(shadow_id).sh_bias; + + /* Metal: Ensure comparison depth is in [0, 1] range */ +#ifdef GPU_METAL + compare_z = clamp(compare_z, 0.0, 1.0); +#endif + + coord = vec4(shpos.xy, tex_id + float(cascade), compare_z); + #ifdef USE_SHADOW_ID float id_sample = sample_ID_texture(shadowCascadeIDTexture, coord.xyz, match_shadow_id); vis += min(texture(shadowCascadeTexture, coord) + id_sample, 1.0) * (1.0 - blend); @@ -245,7 +267,14 @@ float sample_cascade_shadow(int shadow_id, vec3 P, bool match_shadow_id) cascade = min(3, cascade + 1); /* Second cascade. */ shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0); - coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias); + compare_z = shpos.z - sd(shadow_id).sh_bias; + + /* Metal: Ensure comparison depth is in [0, 1] range */ +#ifdef GPU_METAL + compare_z = clamp(compare_z, 0.0, 1.0); +#endif + + coord = vec4(shpos.xy, tex_id + float(cascade), compare_z); #ifdef USE_SHADOW_ID id_sample = sample_ID_texture(shadowCascadeIDTexture, coord.xyz, match_shadow_id); vis += min(texture(shadowCascadeTexture, coord) + id_sample, 1.0) * blend; diff --git a/source/blender/draw/engines/gooengine/shaders/prepass_frag.glsl b/source/blender/draw/engines/gooengine/shaders/prepass_frag.glsl index 38adaf8599d..3f7cbcfd771 100644 --- a/source/blender/draw/engines/gooengine/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/gooengine/shaders/prepass_frag.glsl @@ -92,7 +92,15 @@ void main() } #endif - resource_id_out = resource_id; + /* resource_id_out = resource_id; */ + /* GooEngine Fix: Output Normal to RG16 buffer (Loc 1). + * Use viewNormal from vertex shader interface, which is always initialized. + * g_data.N is only initialized when USE_ALPHA_HASH is defined. */ +#ifndef SHADOW_PASS + /* viewNormal comes from surface_lib interface, already in view space. */ + vec3 N = normalize(viewNormal); + out_normal = normal_encode(N, vec3(0.0)); +#endif } /* Passthrough. */ diff --git a/source/blender/gpu/GPU_material.hh b/source/blender/gpu/GPU_material.hh index d79ed708b49..7d4cf5d43a0 100644 --- a/source/blender/gpu/GPU_material.hh +++ b/source/blender/gpu/GPU_material.hh @@ -94,6 +94,7 @@ enum eGPUMaterialFlag { /* HACK(fclem) Tells the environment texture node to not bail out if empty. */ GPU_MATFLAG_LOOKDEV_HACK = (1 << 30), + GPU_MATFLAG_SET_DEPTH = (1 << 31), }; ENUM_OPERATORS(eGPUMaterialFlag, GPU_MATFLAG_LOOKDEV_HACK);