diff --git a/src/common/engine/sc_man.cpp b/src/common/engine/sc_man.cpp index ac7b4799f..bb5344f76 100644 --- a/src/common/engine/sc_man.cpp +++ b/src/common/engine/sc_man.cpp @@ -639,6 +639,20 @@ bool FScanner::CheckToken (int token, bool evaluate) return false; } +bool FScanner::PeekToken (int token, bool evaluate) +{ + if (GetToken (evaluate)) + { + if (TokenType == token) + { + UnGet (); + return true; + } + UnGet (); + } + return false; +} + //========================================================================== // // FScanner :: GetNumber diff --git a/src/common/engine/sc_man.h b/src/common/engine/sc_man.h index 5873523a1..a385b983c 100644 --- a/src/common/engine/sc_man.h +++ b/src/common/engine/sc_man.h @@ -114,6 +114,7 @@ public: void TokenMustBe(int token); void MustGetToken(int token, bool evaluate = false); bool CheckToken(int token, bool evaluate = false); + bool PeekToken(int token, bool evaluate = false); bool CheckTokenId(ENamedName id); bool GetNumber(bool evaluate = false); diff --git a/src/common/rendering/hwrenderer/data/hw_materialstate.h b/src/common/rendering/hwrenderer/data/hw_materialstate.h index de0d6ada2..3a308eaa8 100644 --- a/src/common/rendering/hwrenderer/data/hw_materialstate.h +++ b/src/common/rendering/hwrenderer/data/hw_materialstate.h @@ -8,10 +8,11 @@ class FMaterial; struct FMaterialState { FMaterial* mMaterial = nullptr; - int mClampMode; - int mTranslation; - int mOverrideShader; - bool mChanged; + int mClampMode = 0; + int mTranslation = CLAMP_NONE; + int mOverrideShader = -1; + bool mChanged = false; + GlobalShaderAddr globalShaderAddr = {0, 3, 0}; // null global shader entry, TODO void Reset() { @@ -20,5 +21,6 @@ struct FMaterialState mClampMode = CLAMP_NONE; mOverrideShader = -1; mChanged = false; + globalShaderAddr = {0, 3, 0}; } }; diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index c23e5a6f8..21c8c3ef2 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -541,17 +541,7 @@ public: } private: - void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader) - { - mMaterial.mMaterial = mat; - mMaterial.mClampMode = clampmode; - mMaterial.mTranslation = translation; - mMaterial.mOverrideShader = overrideshader; - mMaterial.mChanged = true; - mTextureModeFlags = mat->GetLayerFlags(); - auto scale = mat->GetDetailScale(); - mSurfaceUniforms.uDetailParms = { scale.X, scale.Y, 2, 0 }; - } + void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader); public: void SetMaterial(FGameTexture* tex, EUpscaleFlags upscalemask, int scaleflags, int clampmode, int translation, int overrideshader) diff --git a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp index d09fc6185..7a55e9918 100644 --- a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp +++ b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp @@ -328,16 +328,18 @@ VkMaterial::DescriptorEntry& VkMaterial::GetDescriptorEntry(const FMaterialState auto base = Source(); int clampmode = state.mClampMode; int translation = state.mTranslation; + GlobalShaderAddr globalShaderAddr = state.globalShaderAddr; auto translationp = IsLuminosityTranslation(translation)? translation : intptr_t(GPalette.GetTranslation(GetTranslationType(translation), GetTranslationIndex(translation))); clampmode = base->GetClampMode(clampmode); for (auto& set : mDescriptorSets) { - if (set.clampmode == clampmode && set.remap == translationp) return set; + if (set.clampmode == clampmode && set.remap == translationp && set.globalShaderAddr == globalShaderAddr) return set; } - int numLayers = NumLayers(); + auto globalshader = GetGlobalShader(globalShaderAddr); + int numLayersMat = *globalshader ? NumNonMaterialLayers() : NumLayers(); auto descriptors = fb->GetDescriptorSetManager(); auto* sampler = fb->GetSamplerManager()->Get(clampmode); @@ -348,12 +350,27 @@ VkMaterial::DescriptorEntry& VkMaterial::GetDescriptorEntry(const FMaterialState if (!(layer->scaleFlags & CTF_Indexed)) { - for (int i = 1; i < numLayers; i++) + for (int i = 1; i < numLayersMat; i++) { auto syslayer = static_cast(GetLayer(i, 0, &layer)); auto syslayerimage = syslayer->GetImage(layer->layerTexture, 0, layer->scaleFlags); descriptors->AddBindlessTextureIndex(syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode)); } + + if(*globalshader) + { + size_t i = 0; + for (auto& texture : globalshader->CustomShaderTextures) + { + if (texture != nullptr) + { + VkHardwareTexture *tex = static_cast(texture.get()->GetHardwareTexture(0, 0)); + VkTextureImage *img = tex->GetImage(texture.get(), 0, 0); + descriptors->AddBindlessTextureIndex(img->View.get(), fb->GetSamplerManager()->Get(globalshader->CustomShaderTextureSampling[i], clampmode)); + } + i++; + } + } } else { @@ -363,9 +380,8 @@ VkMaterial::DescriptorEntry& VkMaterial::GetDescriptorEntry(const FMaterialState auto syslayerimage = syslayer->GetImage(layer->layerTexture, 0, layer->scaleFlags); descriptors->AddBindlessTextureIndex(syslayerimage->View.get(), fb->GetSamplerManager()->Get(GetLayerFilter(i), clampmode)); } - numLayers = 3; } - mDescriptorSets.emplace_back(clampmode, translationp, bindlessIndex); + mDescriptorSets.emplace_back(clampmode, translationp, bindlessIndex, globalShaderAddr); return mDescriptorSets.back(); } diff --git a/src/common/rendering/vulkan/textures/vk_hwtexture.h b/src/common/rendering/vulkan/textures/vk_hwtexture.h index 19dd71dc5..b11c031bb 100644 --- a/src/common/rendering/vulkan/textures/vk_hwtexture.h +++ b/src/common/rendering/vulkan/textures/vk_hwtexture.h @@ -78,12 +78,14 @@ private: int clampmode; intptr_t remap; int bindlessIndex; + GlobalShaderAddr globalShaderAddr; - DescriptorEntry(int cm, intptr_t f, int index) + DescriptorEntry(int cm, intptr_t f, int index, GlobalShaderAddr addr) { clampmode = cm; remap = f; bindlessIndex = index; + globalShaderAddr = addr; } }; diff --git a/src/common/rendering/vulkan/vk_renderdevice.cpp b/src/common/rendering/vulkan/vk_renderdevice.cpp index 292e67246..3454c2a4e 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.cpp +++ b/src/common/rendering/vulkan/vk_renderdevice.cpp @@ -656,10 +656,20 @@ void VulkanRenderDevice::DownloadLightmap(int arrayIndex, uint16_t* buffer) int VulkanRenderDevice::GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation) { + GlobalShaderAddr addr; + auto globalshader = GetGlobalShader(material->GetShaderIndex(), nullptr, addr); + FMaterialState materialState; materialState.mMaterial = material; materialState.mClampMode = clampmode; materialState.mTranslation = translation; + + if(addr.type == 1 && *globalshader) + { // handle per-map global shaders + materialState.globalShaderAddr = addr; + materialState.mOverrideShader = globalshader->shaderindex; + } + return static_cast(material)->GetBindlessIndex(materialState); } diff --git a/src/common/textures/gametexture.h b/src/common/textures/gametexture.h index 3f929fca1..f4e8dd3bd 100644 --- a/src/common/textures/gametexture.h +++ b/src/common/textures/gametexture.h @@ -94,10 +94,38 @@ struct GlobalShaderDesc int shaderindex = -1; RefCountedPtr CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; MaterialLayerSampling CustomShaderTextureSampling[MAX_CUSTOM_HW_SHADER_TEXTURES]; + + operator bool() const { return shaderindex >= 0; } }; +struct GlobalShaderAddr +{ + int16_t num; // [0 - NUM_BUILTIN_SHADERS) + int16_t type; // 0 - global, 1 - map, 2 - class, 3 - invalid + int32_t name; // global - unused, 1 - map name index, 2 - class name index + + bool operator<(GlobalShaderAddr other) + { + return reinterpret_cast(*this) < reinterpret_cast(other); + } + + bool operator==(GlobalShaderAddr other) + { + return reinterpret_cast(*this) == reinterpret_cast(other); + } +}; + +static_assert(sizeof(GlobalShaderAddr) == sizeof(uint64_t)); + +extern const GlobalShaderDesc nullglobalshader; extern GlobalShaderDesc globalshaders[NUM_BUILTIN_SHADERS]; +const GlobalShaderAddr GetGlobalShaderAddr(int shaderNum, class PClass * curActor); // full checks +const GlobalShaderDesc * GetGlobalShader(int shaderNum, class PClass * curActor, GlobalShaderAddr &addr); // full checks +const GlobalShaderDesc * GetGlobalShader(int shaderNum, class PClass * curActor); // full checks +const GlobalShaderDesc * GetGlobalShader(GlobalShaderAddr index); // no checks, only use with addrs gotten from GetGlobalShaderAddr +void CleanupGlobalShaders(); + // Refactoring helper to allow piece by piece adjustment of the API class FGameTexture { diff --git a/src/common/textures/hw_material.cpp b/src/common/textures/hw_material.cpp index b3dbff623..1e7e635d8 100644 --- a/src/common/textures/hw_material.cpp +++ b/src/common/textures/hw_material.cpp @@ -28,12 +28,46 @@ #include "texturemanager.h" #include "c_cvars.h" #include "v_video.h" +#include "hw_renderstate.h" CVAR(Bool, gl_customshader, true, 0); TArray usershaders; + +void FRenderState::SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader) +{ + mMaterial.mMaterial = mat; + mMaterial.mClampMode = clampmode; + mMaterial.mTranslation = translation; + if(overrideshader > 0) + { + mMaterial.mOverrideShader = overrideshader; + mMaterial.globalShaderAddr = {0, 3, 0}; + } + else + { // handle per-map global shaders + GlobalShaderAddr addr; + auto globalshader = GetGlobalShader(mat->GetShaderIndex(), nullptr, addr); + + if(addr.type == 1 && globalshader->shaderindex >= 0) + { + mMaterial.mOverrideShader = globalshader->shaderindex; + mMaterial.globalShaderAddr = addr; + } + else + { + mMaterial.mOverrideShader = -1; + mMaterial.globalShaderAddr = {0, 3, 0}; + } + } + mMaterial.mChanged = true; + mTextureModeFlags = mat->GetLayerFlags(); + auto scale = mat->GetDetailScale(); + mSurfaceUniforms.uDetailParms = { scale.X, scale.Y, 2, 0 }; +} + //=========================================================================== // // Constructor @@ -120,11 +154,15 @@ FMaterial::FMaterial(FGameTexture * tx, int scaleflags) mTextureLayers.Push({ placeholder->GetTexture(), 0, -1, MaterialLayerSampling::Default }); } + mNumNonMaterialLayers = mTextureLayers.Size(); + auto index = tx->GetShaderIndex(); + const auto globalshader = mShaderIndex < FIRST_USER_SHADER ? &globalshaders[mShaderIndex] : &nullglobalshader; + if (gl_customshader) { - if (index >= FIRST_USER_SHADER || (mShaderIndex < FIRST_USER_SHADER && globalshaders[mShaderIndex].shaderindex >= FIRST_USER_SHADER)) + if (index >= FIRST_USER_SHADER || globalshader->shaderindex >= FIRST_USER_SHADER) { if (index >= FIRST_USER_SHADER && usershaders[index - FIRST_USER_SHADER].shaderType == mShaderIndex) // Only apply user shader if it matches the expected material @@ -136,24 +174,26 @@ FMaterial::FMaterial(FGameTexture * tx, int scaleflags) { if (texture != nullptr) { - mTextureLayers.Push({ texture.get(), 0, -1, tx->Layers->CustomShaderTextureSampling[i++]}); // scalability should be user-definable. + mTextureLayers.Push({ texture.get(), 0, -1, tx->Layers->CustomShaderTextureSampling[i]}); // scalability should be user-definable. } + i++; } } mShaderIndex = index; } - else if(mShaderIndex < FIRST_USER_SHADER && globalshaders[mShaderIndex].shaderindex >= FIRST_USER_SHADER) + else if(mShaderIndex < FIRST_USER_SHADER && globalshader->shaderindex >= FIRST_USER_SHADER) { size_t i = 0; - for (auto& texture : globalshaders[mShaderIndex].CustomShaderTextures) + for (auto& texture : globalshader->CustomShaderTextures) { if (texture != nullptr) { - mTextureLayers.Push({ texture.get(), 0, -1, globalshaders[mShaderIndex].CustomShaderTextureSampling[i++]}); // scalability should be user-definable. + mTextureLayers.Push({ texture.get(), 0, -1, globalshader->CustomShaderTextureSampling[i]}); // scalability should be user-definable. } + i++; } - mShaderIndex = globalshaders[mShaderIndex].shaderindex; + mShaderIndex = globalshader->shaderindex; } } } diff --git a/src/common/textures/hw_material.h b/src/common/textures/hw_material.h index 11769d696..7cf350623 100644 --- a/src/common/textures/hw_material.h +++ b/src/common/textures/hw_material.h @@ -30,6 +30,8 @@ class FMaterial int mLayerFlags = 0; int mScaleFlags; + int mNumNonMaterialLayers = 0; + public: FGameTexture *sourcetex; // the owning texture. @@ -65,6 +67,11 @@ public: return mTextureLayers.Size(); } + int NumNonMaterialLayers() const + { + return mNumNonMaterialLayers; + } + IHardwareTexture *GetLayer(int i, int translation, MaterialLayerInfo **pLayer = nullptr) const; MaterialLayerSampling GetLayerFilter(int index) const diff --git a/src/d_main.cpp b/src/d_main.cpp index e6484ff2b..ff161c624 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -4011,10 +4011,7 @@ void D_Cleanup() *(afunc->VMPointer) = NULL; }); - for(auto &gshader : globalshaders) - { - gshader = {}; - } + CleanupGlobalShaders(); GC::DelSoftRootHead(); diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 1e993581b..a1446ce42 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -59,8 +59,98 @@ void ParseColorization(FScanner& sc); extern TDeletingArray LightDefaults; extern int AttenuationIsSet; +const GlobalShaderDesc nullglobalshader = {}; + GlobalShaderDesc globalshaders[NUM_BUILTIN_SHADERS]; +TMap mapshaders[NUM_BUILTIN_SHADERS]; +TMap classshaders[NUM_BUILTIN_SHADERS]; + +const GlobalShaderDesc * GetGlobalShader(int shaderNum, PClass * curActor, GlobalShaderAddr &addr) +{ + if(shaderNum < NUM_BUILTIN_SHADERS) + { + GlobalShaderDesc * shader; + if(curActor) + { + while(curActor->TypeName != NAME_Actor) + { + shader = classshaders[shaderNum].CheckKey(curActor->TypeName); + if(shader && shader->shaderindex > 0) + { + addr = {int16_t(shaderNum), 2, curActor->TypeName.GetIndex()}; + return shader; + } + + curActor = curActor->ParentClass; + } + } + + FName MapName = level.MapName; + shader = mapshaders[shaderNum].CheckKey(MapName); + if(shader && shader->shaderindex > 0) + { + addr = {int16_t(shaderNum), 1, MapName.GetIndex()}; + return shader; + } + + addr = {int16_t(shaderNum), 0, 0}; + return &globalshaders[shaderNum]; + } + else + { + addr = {0, 3, 0}; + return &nullglobalshader; + } +} + +const GlobalShaderAddr GetGlobalShaderAddr(int shaderNum, PClass * curActor) +{ + GlobalShaderAddr addr; + GetGlobalShader(shaderNum, curActor, addr); + return addr; +} + +const GlobalShaderDesc * GetGlobalShader(int shaderNum, PClass * curActor) +{ + GlobalShaderAddr dummy; + return GetGlobalShader(shaderNum, curActor, dummy); +} + + + +const GlobalShaderDesc * GetGlobalShader(GlobalShaderAddr index) +{ + if(index.num > NUM_BUILTIN_SHADERS || index.type > 2) return &nullglobalshader; + + if(index.type == 0) return &globalshaders[index.num]; + + FName name {ENamedName(index.name)}; + + assert((index.type == 1 && mapshaders[index.num].CheckKey(name)) || (index.type == 2 && classshaders[index.num].CheckKey(name)) || index.type == 3); + + if(index.type == 1) return mapshaders[index.num].CheckKey(name); + if(index.type == 2) return classshaders[index.num].CheckKey(name); +} + +void CleanupGlobalShaders() +{ + for(auto &gshader : globalshaders) + { + gshader = {}; + } + + for(auto &gshader2 : mapshaders) + { + gshader2.Clear(); + } + + for(auto &gshader3 : classshaders) + { + gshader3.Clear(); + } +} + struct ExtraUniformCVARData { bool isPP; @@ -1413,6 +1503,8 @@ class GLDefsParser TArray globaltargets; FString str_globaltargets; + TArray globalshader_maps; + TArray globalshader_classes; UserShaderDesc usershader; TArray texNameList; @@ -1528,6 +1620,25 @@ class GLDefsParser { sc.ScriptMessage("Invalid globalshader target\n", sc.String); } + + if(sc.CheckString("map")) + { + do + { + sc.MustGetString(); + globalshader_maps.Push(sc.String); + } + while(!sc.PeekToken('{')); + } + else if(sc.CheckString("class")) + { + do + { + sc.MustGetString(); + globalshader_classes.Push(sc.String); + } + while(!sc.PeekToken('{')); + } } else { @@ -1847,17 +1958,43 @@ class GLDefsParser } if(is_globalshader) { - if(fileSystem.GetFileContainer(workingLump) > fileSystem.GetMaxIwadNum()) + if(fileSystem.GetFileContainer(workingLump) > fileSystem.GetMaxIwadNum() && globalshader_maps.Size() == 0 && globalshader_classes.Size() == 0) { sc.ScriptError("globalshader only supported on iwad"); return; } else for(int target : globaltargets) { - if(globalshaders[target].shaderindex >= 0) + + if(globalshader_maps.Size() > 0) { - sc.ScriptError("globalshader already exists for '%s'", str_globaltargets.GetChars()); - return; + for(FName map : globalshader_maps) + { + if(mapshaders[target][map].shaderindex >= 0) + { + sc.ScriptError("globalshader '%s' already exists for map '%s'", str_globaltargets.GetChars(), map.GetChars()); + return; + } + } + } + else if(globalshader_classes.Size() > 0) + { + for(FName clazz : globalshader_classes) + { + if(classshaders[target][clazz].shaderindex >= 0) + { + sc.ScriptError("globalshader '%s' already exists for class '%s'", str_globaltargets.GetChars(), clazz.GetChars()); + return; + } + } + } + else + { + if(globalshaders[target].shaderindex >= 0) + { + sc.ScriptError("globalshader '%s' already exists", str_globaltargets.GetChars()); + return; + } } } @@ -1866,7 +2003,7 @@ class GLDefsParser int lump = fileSystem.CheckNumForFullName(usershader.shader.GetChars()); if (lump == -1) { - sc.ScriptError("inexistent shader lump '%s' in globalshader '%s'", usershader.shader.GetChars(), str_globaltargets.GetChars()); + sc.ScriptError("inexistent shader lump '%s' in global shader '%s'", usershader.shader.GetChars(), str_globaltargets.GetChars()); return; } @@ -1875,7 +2012,7 @@ class GLDefsParser int lump = fileSystem.CheckNumForFullName(usershader.vertshader.GetChars()); if (lump == -1) { - sc.ScriptError("inexistent vertex shader lump '%s' in globalshader '%s'", usershader.vertshader.GetChars(), str_globaltargets.GetChars()); + sc.ScriptError("inexistent vertex shader lump '%s' in global shader '%s'", usershader.vertshader.GetChars(), str_globaltargets.GetChars()); return; } } @@ -1906,7 +2043,26 @@ class GLDefsParser usershader.shaderType = MaterialShaderIndex(target); - globalshaders[target].shaderindex = usershaders.Push(usershader) + FIRST_USER_SHADER; + int shaderIndex = usershaders.Push(usershader) + FIRST_USER_SHADER; + + if(globalshader_maps.Size() > 0) + { + for(FName map : globalshader_maps) + { + mapshaders[target][map].shaderindex = shaderIndex; + } + } + else if(globalshader_classes.Size() > 0) + { + for(FName clazz : globalshader_classes) + { + classshaders[target][clazz].shaderindex = shaderIndex; + } + } + else + { + globalshaders[target].shaderindex = shaderIndex; + } if(hasUniforms) { @@ -1917,8 +2073,27 @@ class GLDefsParser { if (mlay.CustomShaderTextures[i]) { - globalshaders[target].CustomShaderTextureSampling[i] = mlay.CustomShaderTextureSampling[i]; - globalshaders[target].CustomShaderTextures[i] = mlay.CustomShaderTextures[i]->GetTexture(); + if(globalshader_maps.Size() > 0) + { + for(FName map : globalshader_maps) + { + mapshaders[target][map].CustomShaderTextureSampling[i] = mlay.CustomShaderTextureSampling[i]; + mapshaders[target][map].CustomShaderTextures[i] = mlay.CustomShaderTextures[i]->GetTexture(); + } + } + else if(globalshader_classes.Size() > 0) + { + for(FName clazz : globalshader_classes) + { + classshaders[target][clazz].CustomShaderTextureSampling[i] = mlay.CustomShaderTextureSampling[i]; + classshaders[target][clazz].CustomShaderTextures[i] = mlay.CustomShaderTextures[i]->GetTexture(); + } + } + else + { + globalshaders[target].CustomShaderTextureSampling[i] = mlay.CustomShaderTextureSampling[i]; + globalshaders[target].CustomShaderTextures[i] = mlay.CustomShaderTextures[i]->GetTexture(); + } } }