diff --git a/engine/modules/engine/render/include/render/asset/material.h b/engine/modules/engine/render/include/render/asset/material.h index 991ad30..453cd79 100644 --- a/engine/modules/engine/render/include/render/asset/material.h +++ b/engine/modules/engine/render/include/render/asset/material.h @@ -43,5 +43,30 @@ namespace api { Material* operator->() { return mMaterial.Ptr(); } + template + bool SetResource(MaterialResource* pResource, const T& t) { + if (!pResource) { + return false; + } + auto obj = pResource->obj; +#ifdef API_DEBUG + if (!obj.Check(meta_info())) { + throw std::runtime_error("Error: SetResource with error class type"); + } +#endif // API_DEBUG + *(obj.CastTo()) = t; + pResource->isDirty = true; + return true; + } + template + bool SetStaticResource(Name name,const T& t) { + auto pResource = mInfo.staticBlock.FindMaterialResource(name); + return SetResource(pResource, t); + } + template + bool SetClassResource(Name name, const T& t) { + auto pResource = mInfo.classBlock.FindMaterialResource(name); + return SetResource(pResource, t); + } }; }; \ No newline at end of file diff --git a/engine/modules/engine/render/include/render/asset/ubo.h b/engine/modules/engine/render/include/render/asset/ubo.h index 2e0c8e5..de8f3c9 100644 --- a/engine/modules/engine/render/include/render/asset/ubo.h +++ b/engine/modules/engine/render/include/render/asset/ubo.h @@ -2,12 +2,19 @@ #include "vertex.h" namespace api{ struct ubMetaInfo { - uint32_t set; - uint32_t binding; - bool isShared{false}; + uint32_t set{0}; + uint32_t binding{0}; + bool isUnique{false}; + static ubMetaInfo FromField(const refl::FieldPtr& field) { + refl::Any meta = field.GetMeta(); + if (meta) { + return meta.CastTo(); + } + return {}; + } }; struct ubSimple { - UPROPERTY({}, api::ubMetaInfo{ .set = 0, .binding = 0 }) + UPROPERTY({}, api::ubMetaInfo{ .set = 0, .binding = 0 , .isUnique = true}) Vector4 uColor; }; } diff --git a/engine/modules/engine/render/include/render/type.h b/engine/modules/engine/render/include/render/type.h index 7e190e6..f83477e 100644 --- a/engine/modules/engine/render/include/render/type.h +++ b/engine/modules/engine/render/include/render/type.h @@ -1,7 +1,8 @@ #pragma once -#include "pmr/name.h" +#include "refl/pch.h" #include "tinyimageformat/tinyimageformat_base.h" #include +//#include namespace api { using pmr::Name; enum class GraphicsAPI @@ -97,16 +98,61 @@ namespace api { VERTEX = 0x1, FRAGMENT = 0x2, }; - struct MaterialInfo { + struct MaterialResource { + Name name; + bool isDirty; + bool isUnique; + refl::Any obj; + void* Ptr()const { + return (void*)obj.ptr; + } + uint32_t Size()const { + return obj.cls ? obj.cls->size : 0; + } + }; + struct MaterialResourceBlock { uint32_t count; - void* pHeader; - char* pFlag; - struct Header { - Name name; - uint32_t size; - void* pCpuData; - void* pMappingData; - }; + uint32_t blockSize; + MaterialResource* pResource; + static MaterialResourceBlock FromFields(std::span fields); + operator bool() { + return pResource; + } + MaterialResource* FindMaterialResource(Name name) { + for (uint32_t i = 0; i < count; i++) { + MaterialResource* res = (pResource + i); + if (res->name == name) { + return res; + } + } + return nullptr; + } + void Upload(void** ppMappingAddr) { + for (uint32_t i = 0; i < count; i++) { + auto res = pResource[i]; + if (res.isDirty || res.isUnique) { + memcpy(*(ppMappingAddr + i), res.Ptr(), res.Size()); + } + } + } + }; + struct MaterialGpuResourceBlock { + uint16_t blockSize; + uint16_t bufferOffset; + bool isInit; + void** pMappingAddr; + operator bool() { + return isInit; + } + void* BufferPtr() { + return (char*)pMappingAddr + bufferOffset; + } + }; + struct MaterialInfo { + bool isInit = false; + MaterialGpuResourceBlock gpuBlock; + MaterialResourceBlock classBlock; + MaterialResourceBlock staticBlock; }; struct ShaderDescriptorLayout { Name name; diff --git a/engine/modules/engine/render/src/pass/demo_pass.cpp b/engine/modules/engine/render/src/pass/demo_pass.cpp index b324acf..0599582 100644 --- a/engine/modules/engine/render/src/pass/demo_pass.cpp +++ b/engine/modules/engine/render/src/pass/demo_pass.cpp @@ -46,9 +46,8 @@ namespace api { mesh->SetMaterial(material); RenderAPI::Ptr()->SetStaticMesh(*mesh); RenderAPI::Ptr()->SetUpMaterialInstance(mesh->GetMaterialInstance()); - ResourceSystem::Ptr()->AddDirtyFile("/engine/assets/shader/simple.vert"); - ResourceSystem::Ptr()->AddDirtyFile("/engine/assets/shader/simple.frag"); - ResourceSystem::Ptr()->SaveDirtyFile(); + auto materialInstance = mesh->GetMaterialInstance(); + materialInstance.SetClassResource(Name("uColor"), Vector4{1,0,0,1}); } auto& surface = graph.mSurface; ctx->SetViewport(0.0f, 0.0f,(float)surface.width,(float)surface.height, 0.f, 1.f); diff --git a/engine/modules/engine/render/src/render.cpp b/engine/modules/engine/render/src/render.cpp index 9409888..b890588 100644 --- a/engine/modules/engine/render/src/render.cpp +++ b/engine/modules/engine/render/src/render.cpp @@ -1,4 +1,39 @@ +#include "render/type.h" +#include "render/asset/ubo.h" +#include +namespace api { + MaterialResourceBlock MaterialResourceBlock::FromFields(std::span fields) + { + uint32_t count = fields.size(); + if (!count) { + return {}; + } + uint32_t blockSize = meta_align_size(sizeof(MaterialResource) * count); + for (auto& field : fields) { + blockSize += meta_align_size(field.type->size); + } + MaterialResource* refResource = (MaterialResource*)xmalloc(blockSize); + char* pObj = (char*)refResource + meta_align_size(sizeof(MaterialResource) * count); + MaterialResource* pResource = refResource; + for (auto& field : fields) { + new(pResource)MaterialResource{}; + pResource->name = field.name; + pResource->obj = field.Construct(pObj); + pResource->isDirty = false; + ubMetaInfo ub = ubMetaInfo::FromField(field); + pResource->isUnique = ub.isUnique; + pObj += meta_align_size(field.type->size); + pResource++; + } + if (count > 1) { + std::sort(refResource, refResource, [](const MaterialResource& res1, const MaterialResource& res2) { + return res1.name < res2.name; + }); + } + return { count , blockSize , refResource }; + } +} #ifdef RENDER_API_VAL #include "renderapi_impl.inl" #include "window_impl.inl" -#endif // +#endif // \ No newline at end of file diff --git a/engine/modules/engine/render/src/tool/glsl_to_spirv.cpp b/engine/modules/engine/render/src/tool/glsl_to_spirv.cpp index 1148146..388dac0 100644 --- a/engine/modules/engine/render/src/tool/glsl_to_spirv.cpp +++ b/engine/modules/engine/render/src/tool/glsl_to_spirv.cpp @@ -67,7 +67,7 @@ namespace api }; auto FindFieldFn = [](FieldPtrSpan fieldList, uint32_t set, uint32_t binding) -> FindFieldResult{ for (auto& field : fieldList) { - ubMetaInfo info = field.GetMeta().CastTo(); + ubMetaInfo info = ubMetaInfo::FromField(field); if (info.set == set && info.binding == binding) { return { &field , info}; } @@ -84,7 +84,6 @@ namespace api layout.stageFlags = stage; if (auto result = FindFieldFn(fieldList, layout.set, layout.binding)) { layout.name = result.field->name; - layout.isShared = result.info.isShared; } return layout; }; diff --git a/engine/modules/engine/zlib/include/refl/detail/any.inl b/engine/modules/engine/zlib/include/refl/detail/any.inl index d1a2838..e8109a4 100644 --- a/engine/modules/engine/zlib/include/refl/detail/any.inl +++ b/engine/modules/engine/zlib/include/refl/detail/any.inl @@ -144,4 +144,15 @@ namespace refl{ memset(ptr, 0, size); return true; } + inline Any FieldPtr::Construct(void* ptr) const + { + Any obj{ ptr, type }; + if (data.member.value && flag & FIELD_MEMBER_FLAG) { + obj.Construct(data.member.value); + } + else { + obj.Construct({}); + } + return obj; + } } \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/refl/detail/field.h b/engine/modules/engine/zlib/include/refl/detail/field.h index 3642fb9..01c4971 100644 --- a/engine/modules/engine/zlib/include/refl/detail/field.h +++ b/engine/modules/engine/zlib/include/refl/detail/field.h @@ -22,9 +22,10 @@ namespace refl { enum FieldFlag :uint32_t { FIELD_NONE_FLAG = 0, FIELD_MEMBER_FLAG = 1 << 0, - FIELD_METHOD_FLAG = 1 << 1, - FIELD_METHOD_VALUE_FLAG = 1 << 2, - FIELD_CTOR_FLAG = 1 << 3, + FIELD_STATIC_MEMBER_FLAG = 1 << 1, + FIELD_METHOD_FLAG = 1 << 2, + FIELD_METHOD_VALUE_FLAG = 1 << 3, + FIELD_CTOR_FLAG = 1 << 4, }; struct FieldPtr { union Data @@ -42,12 +43,16 @@ namespace refl { const UClass* type{}; Data data{}; uint32_t flag{}; + bool IsStatic()const { + return flag & FIELD_STATIC_MEMBER_FLAG; + } Offset GetOffset() const { if (flag & FIELD_MEMBER_FLAG) { return data.member.offset; } return 0; } + Any Construct(void* ptr)const; Any GetValue()const { if (flag & FIELD_MEMBER_FLAG) return data.member.value; diff --git a/engine/modules/engine/zlib/include/refl/detail/meta.h b/engine/modules/engine/zlib/include/refl/detail/meta.h index 1dc01fe..1eb0e09 100644 --- a/engine/modules/engine/zlib/include/refl/detail/meta.h +++ b/engine/modules/engine/zlib/include/refl/detail/meta.h @@ -35,6 +35,9 @@ namespace refl { template static FieldPtr MemberField(T Obj::* ptr, std::string_view name, const MemberData& data = {}); + template + static FieldPtr MemberField(T* ptr, std::string_view name, const MemberData& data = {}); + template static FieldPtr MethodField(R(*ptr)(Args...), std::string_view name, const MethodData& data = {}); diff --git a/engine/modules/engine/zlib/include/refl/detail/meta.inl b/engine/modules/engine/zlib/include/refl/detail/meta.inl index 1df78ae..be7e318 100644 --- a/engine/modules/engine/zlib/include/refl/detail/meta.inl +++ b/engine/modules/engine/zlib/include/refl/detail/meta.inl @@ -57,6 +57,21 @@ namespace refl { } return FieldPtr{ name, cls, member, flag }; } + template + inline FieldPtr MetaHelp::MemberField(T* ptr, std::string_view name, const MemberData& data) + { + const UClass* cls = meta_info(); + uint32_t flag = FIELD_STATIC_MEMBER_FLAG; + MemberData member; + member.offset = reinterpret_cast(&(reinterpret_cast(0)->*ptr)); + if (data.value) { + member.value = data.value.New(MetaGlobalPool()); + } + if (data.meta) { + member.meta = data.meta.New(MetaGlobalPool()); + } + return FieldPtr{ name, cls, member, flag }; + } template inline FieldPtr MetaHelp::MethodField(R(*ptr)(Args...), std::string_view name, const MethodData& data) { diff --git a/engine/modules/engine/zlib/include/refl/detail/uclass.h b/engine/modules/engine/zlib/include/refl/detail/uclass.h index 622d660..f0b05f9 100644 --- a/engine/modules/engine/zlib/include/refl/detail/uclass.h +++ b/engine/modules/engine/zlib/include/refl/detail/uclass.h @@ -17,6 +17,7 @@ namespace refl { enum EFieldFind :uint32_t { FIND_ALL_FIELD = 0, FIND_ALL_MEMBER, + FIND_ALL_MEMBER_WITH_STATIC, FIND_ALL_METHOD, FIND_FIELD, FIND_CTOR, diff --git a/engine/modules/engine/zlib/include/refl/detail/uclass.inl b/engine/modules/engine/zlib/include/refl/detail/uclass.inl index 29f3e10..c30dc91 100644 --- a/engine/modules/engine/zlib/include/refl/detail/uclass.inl +++ b/engine/modules/engine/zlib/include/refl/detail/uclass.inl @@ -178,16 +178,19 @@ namespace refl { span GetFields(EFieldFind find, const Name& name) const { constexpr int length = std::tuple_size::value; constexpr int MemberCount = MetaImpl::MemberCount(); + constexpr int AllMemberCount = MemberCount + MetaImpl::StaticMemberCount(); constexpr int CtorCount = MetaImpl::CtorCount(); switch (find) { case EFieldFind::FIND_ALL_FIELD: return span(&Fields[0], length); case EFieldFind::FIND_ALL_MEMBER: return span(&Fields[0], MemberCount); + case EFieldFind::FIND_ALL_MEMBER_WITH_STATIC: + return span(&Fields[0], AllMemberCount); case EFieldFind::FIND_ALL_METHOD: - return span(&Fields[MemberCount + CtorCount], length - MemberCount - CtorCount); + return span(&Fields[AllMemberCount + CtorCount], length - AllMemberCount - CtorCount); case EFieldFind::FIND_CTOR: - return span(&Fields[MemberCount], CtorCount); + return span(&Fields[AllMemberCount], CtorCount); case EFieldFind::FIND_FIELD: for (int i = 0; i < length; i++) { if (name == Fields[i].name) { @@ -196,14 +199,14 @@ namespace refl { } return {}; case EFieldFind::FIND_MEMBER: - for (int i = 0; i < MemberCount; i++) { + for (int i = 0; i < AllMemberCount; i++) { if (name == Fields[i].name) { return span(&Fields[i], 1); } } return {}; case EFieldFind::FIND_METHOD: - for (int i = MemberCount + CtorCount; i < length; i++) { + for (int i = AllMemberCount + CtorCount; i < length; i++) { if (name == Fields[i].name) { return span(&Fields[i], 1); } @@ -212,7 +215,7 @@ namespace refl { case EFieldFind::FIND_METHODS: { int first = 0, count = 0; - for (int i = MemberCount + CtorCount; i < length; i++) { + for (int i = AllMemberCount + CtorCount; i < length; i++) { if (name == Fields[i].name) { if (!count) { first = i; diff --git a/engine/modules/render/vulkan/include/vkn/loader/vulkan_glsl_loader.h b/engine/modules/render/vulkan/include/vkn/loader/vulkan_glsl_loader.h index d5d4f4d..e0f9179 100644 --- a/engine/modules/render/vulkan/include/vkn/loader/vulkan_glsl_loader.h +++ b/engine/modules/render/vulkan/include/vkn/loader/vulkan_glsl_loader.h @@ -21,6 +21,6 @@ namespace vkn { static void Init(); api::ResourceBundle LoadFile(api::PackagePath handle, const api::MetaBundle& meta) override; static uint32_t GetShaderLayout(VkDescriptorSetLayoutList& layoutList, pmr::vector& programList); - static void LoadShaderBuffer(pmr::vector& programList, MaterialInfo& info); + static void LoadShaderBuffer(VkDescriptorSetList& setList, pmr::vector& programList, MaterialInfo& info); }; } \ No newline at end of file diff --git a/engine/modules/render/vulkan/src/loader/vulkan_glsl_loader.cpp b/engine/modules/render/vulkan/src/loader/vulkan_glsl_loader.cpp index 21c4083..f66d1fb 100644 --- a/engine/modules/render/vulkan/src/loader/vulkan_glsl_loader.cpp +++ b/engine/modules/render/vulkan/src/loader/vulkan_glsl_loader.cpp @@ -6,10 +6,12 @@ #include "render/asset/material.h" #include "render/tool/glsl_to_spirv.h" #include "render/asset/vertex.h" +#include "render/asset/ubo.h" #include "os/file_handle.h" #include ".render/vkmeta_vertex_gen.inl" using namespace api; namespace vkn { + table MaterialInfoTable; void VulkanGlslLoader::Init() { refl::register_meta(); @@ -93,45 +95,91 @@ namespace vkn { } return count; } - void VulkanGlslLoader::LoadShaderBuffer(pmr::vector& programList, MaterialInfo& info) - { - ShaderDescriptorSet descList{ FramePool() }; - GlslToSpirv::GetShaderLayout(descList, programList); - VulkanAPI* API = VulkanAPI::Ptr(); - size_t count = descList.size(); - if (!count) { - return; - } - size_t header_size = meta_align_size(sizeof(MaterialInfo::Header) * count) + meta_align_size(sizeof(Buffer) * count) + meta_align_size(count); - size_t size = header_size; - for (auto& desc : descList) { - size += meta_align_size(desc.size); - } - size += meta_align_size(sizeof(Buffer) * count); - MaterialInfo::Header* pHeader = (MaterialInfo::Header*)BufferPool()->allocate(size, 8); - Buffer* pBuffer = (Buffer*)((char*)pHeader + meta_align_size(sizeof(MaterialInfo::Header) * count)); - info.count = count; - info.pHeader = pHeader; - info.pFlag = (char*)pBuffer + meta_align_size(sizeof(Buffer) * count); - char* pData = (char*)pHeader + header_size; - for (auto& desc : descList) { - new(pHeader)MaterialInfo::Header{}; + void InitMaterialGpuResourceBlock(MaterialGpuResourceBlock& block, std::span fields) { + block.isInit = true; + uint32_t count = fields.size(); + block.bufferOffset = meta_align_size(sizeof(void*) * count); + uint32_t blockSize = block.bufferOffset + meta_align_size(sizeof(Buffer) * count); + block.pMappingAddr = (void**)BufferPool()->allocate(blockSize, 8); + Buffer* pBuffer = (Buffer*)block.BufferPtr(); + void** ppMappingData = block.pMappingAddr; + for (auto& field : fields) { new(pBuffer)Buffer{}; - pHeader->name = desc.name; - pHeader->size = desc.size; - pHeader->pCpuData = pData; BufferCreator creatorInfo{}; creatorInfo.pBuffer = &pBuffer->buffer; creatorInfo.pAllocation = &pBuffer->allocation; - creatorInfo.ppCpuData = (void**) &pData; - creatorInfo.size = desc.size; + creatorInfo.ppCpuData = ppMappingData; + creatorInfo.size = field.type->size; creatorInfo.memoryUsage = VMA_MEMORY_USAGE_AUTO; creatorInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - Backend::TransferWorker->Invoke(creatorInfo); - pHeader++; - pData += meta_align_size(desc.size); + Backend::TransferWorker->CreateBuffer(creatorInfo); + ppMappingData++; + pBuffer++; } } + void BindDescriptorSetBuffer(MaterialGpuResourceBlock& block, const ShaderDescriptorSet& descList, const VkDescriptorSetList& setList) { + VkWriteDescriptorSet writeDescriptorSet[16] = {}; + VkDescriptorBufferInfo bufferInfoList[16] = {}; + uint32_t descCount = 0; + Buffer* buffer = (Buffer*)block.BufferPtr(); + for (auto& desc : descList) { + auto& bufferInfo = bufferInfoList[descCount]; + bufferInfo.offset = 0; + bufferInfo.range = desc.size; + bufferInfo.buffer = buffer->buffer; + auto& writeDesc = writeDescriptorSet[descCount]; + writeDesc.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDesc.dstSet = setList[desc.set]; + writeDesc.dstBinding = desc.binding; + writeDesc.descriptorType = vkApiGetDescriptorType(desc.type); + writeDesc.descriptorCount = 1; + writeDesc.pBufferInfo = &bufferInfo; + descCount++; + } + vkUpdateDescriptorSets(VulkanAPI::Ptr()->GetBackend().GetDevice().Ptr(), descCount, writeDescriptorSet, 0, nullptr); + } + + MaterialInfo& FindMaterialStaticBlock(const refl::UClass* meta, std::span fields) { + auto it = MaterialInfoTable.find(meta->name); + if (it != MaterialInfoTable.end()) { + return it->second; + } + + uint16_t classMember = 0, staticMember = 0; + for (auto& field : fields) { + if (field.IsStatic()) { + break; + } + classMember++; + } + MaterialResourceBlock staticBlock = MaterialResourceBlock::FromFields(fields.subspan(classMember, fields.size() - classMember)); + MaterialInfo info{ {},{}, staticBlock }; + MaterialInfoTable.emplace(meta->name, info); + return MaterialInfoTable[meta->name]; + } + void VulkanGlslLoader::LoadShaderBuffer(VkDescriptorSetList& setList, pmr::vector& programList, MaterialInfo& material) + { + if (material.isInit) { + return; + } + material.isInit = true; + auto meta = refl::find_info(programList[0]->ubName()); + if (!meta) { + return; + } + auto fields = meta->GetFields(refl::FIND_ALL_MEMBER_WITH_STATIC, Name("")); + auto& info = FindMaterialStaticBlock(meta, fields); + MaterialResourceBlock classBlock = MaterialResourceBlock::FromFields(fields.subspan(0, fields.size() - info.staticBlock.count)); + if (!info.gpuBlock) { + ShaderDescriptorSet descList{ FramePool() }; + GlslToSpirv::GetShaderLayout(descList, programList); + InitMaterialGpuResourceBlock(info.gpuBlock, fields); + BindDescriptorSetBuffer(info.gpuBlock, descList, setList); + } + material.classBlock = classBlock; + material.gpuBlock = info.gpuBlock; + material.staticBlock = info.staticBlock; + } void vkShaderProgram::Load(const pmr::vector& spirv) { VulkanAPI* API = VulkanAPI::Ptr(); diff --git a/engine/modules/render/vulkan/src/vulkan_api.cpp b/engine/modules/render/vulkan/src/vulkan_api.cpp index 8ebae24..67ca6f8 100644 --- a/engine/modules/render/vulkan/src/vulkan_api.cpp +++ b/engine/modules/render/vulkan/src/vulkan_api.cpp @@ -73,17 +73,25 @@ namespace vkn { vkCmdBindVertexBuffers(ptr, 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(ptr, vulkanVAO.indexBuffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindPipeline(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); - if(pipeline.descCount > 0) + if (pipeline.descCount > 0) { + auto& materialInst = mesh.GetMaterialInstance(); + auto& materialInfo = materialInst.GetInfo(); + auto& gpuBlock = materialInfo.gpuBlock; + uint32_t staticCount = materialInfo.staticBlock.count; + materialInfo.staticBlock.Upload(gpuBlock.pMappingAddr); + materialInfo.classBlock.Upload(gpuBlock.pMappingAddr + staticCount); vkCmdBindDescriptorSets(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, 0, pipeline.descCount, pipeline.descList, 0, VK_NULL_HANDLE); + } vkCmdDrawIndexed(ptr, vulkanVAO.indexCount, 1, 0, 0, 0); } void VulkanAPI::SetUpMaterialInstance(MaterialInstance& material) { Shader& shader = *material->GetShader(); + VulkanPipeline& pipeline = PipelineTable[shader.GetGuid()]; pmr::vector programList{ FramePool() }; programList.push_back(shader.GetVertHandle().Ptr()); programList.push_back(shader.GetFragHandle().Ptr()); - VulkanGlslLoader::LoadShaderBuffer(programList, material.GetInfo()); + VulkanGlslLoader::LoadShaderBuffer(pipeline.descList, programList, material.GetInfo()); } void VulkanAPI::LoadShader(Shader& shader, size_t passKey) { diff --git a/engine/xmake/rule_gen/make_gen.lua b/engine/xmake/rule_gen/make_gen.lua index 029b6c3..ceced2c 100644 --- a/engine/xmake/rule_gen/make_gen.lua +++ b/engine/xmake/rule_gen/make_gen.lua @@ -23,6 +23,10 @@ function _listen_gen_file(target, batch, template, macro, define) genfile, sourcefile = batch[1], batch[2] sourcefile = string.lower(sourcefile) local dependfile = target:dependfile(genfile) + if false then + cmd_compile(batch[1], batch[2], template, macro, define) + return + end depend.on_changed( function() cmd_compile(batch[1], batch[2], template, macro, define)