2

I'm struggling to figure out how to set up my hlsl shaders to work with Premake5 and Visual Studio 2017.

I have no idea how to tell Premake5 to compile my hlsl shaders, as a pre-build step.

Here are my goals for this pre-build step:

  • Specify shader model
  • Specify debug/release compilation
  • Only compile files that have changed
  • Produce dxasm files
  • Place resulting *.asms and *.cso in appropriate release/debug folders

Update 1: Investigating a little further I found that Premake5 has buildaction which makes direct reference to FxCompile.

Update 2: Thanks to mukunda. I was able to configure my project perfectly! I had to build premake5 from source in order to get this premake script to run.

Because as of March, 2019 the binary distributed doesn't support shader model greater than 5.

   hf.deactivate_filter()

   files(src_dir.."shaders/*.hlsl")
   shadermodel("6.3")

   shaderassembler("AssemblyCode")

   local shader_dir = _WORKING_DIR.."/"..src_dir.."shaders/%{cfg.buildcfg}/"

   -- HLSL files that don't end with 'Extensions' will be ignored as they will be
   -- used as includes
   filter("files:**.hlsl")
      flags("ExcludeFromBuild")
      shaderobjectfileoutput(shader_dir.."%{file.basename}"..".cso")
      shaderassembleroutput(shader_dir.."%{file.basename}"..".asm")

   filter("files:**_ps.hlsl")
      removeflags("ExcludeFromBuild")
      shadertype("Pixel")

   filter("files:**_vs.hlsl")
      removeflags("ExcludeFromBuild")
      shadertype("Vertex")

   hf.deactivate_filter()

   -- Warnings as errors
   shaderoptions({"/WX"})

Update 3: I figured out how to handle command line parameters.

2 Answers 2

2

I was just messing around with this today. For my HLSL files I have a simple naming scheme:

  • *-p.hlsl for pixel shader files.
  • *-v.hlsl for vertex shader files.
  • *.hlsl for generic files meant to be included by the shader programs. I just use the hlsl extension so that it shows up with proper HLSL syntax highlighting in the editor.

You don't need custom build rules to compile them. Premake seems to be able to output a proper block in the Visual Studio project for using the shader compiler, and then things like only recompiling files that have changed (with #include dependencies) are handled just fine. Here's what my configuration block looks like:

filter { "files:**.hlsl" }
   flags "ExcludeFromBuild"
   shadermodel "5.0"
filter { "files:**-p.hlsl" }
   removeflags "ExcludeFromBuild"
   shadertype "Pixel"
   shaderentry "ForPixel"
filter { "files:**-v.hlsl" }
   removeflags "ExcludeFromBuild"
   shadertype "Vertex"
   shaderentry "ForVertex"
filter {}

Basically sets up the shadermodel and entry points for my files (For DirectX 12 you probably want shadermodel "6.0"). Then I can add my shaders to the project through files:

files { "shaders/**.hlsl"; }

This is all very new stuff in premake, so you won't find much documentation on it. I saw these options here: https://github.com/premake/premake-core/blob/master/modules/vstudio/_preload.lua.

As for your question about exporting the dxasm files, there are some other options such as shaderassembleroutput and shaderassembler, the latter I believe to be the switch that exports the assembly code to the location specified, but I have not done any testing with these to give an elaborate answer.

Unfortunately I'm not really sure how to enable debugging information in the output files, as there doesn't seem to be a premake option for that yet. I was digging through the documentation to look for any way to add the /Zi (enable debug symbols) flag for shader compilation, but I'm not entirely sure if it's possible.

Sign up to request clarification or add additional context in comments.

6 Comments

Could I specify the debugging information with shaderoptions? Or does shaderoptions do something else entirely? Also, thank you very much I'll make to test this all as soon as I can.
I tried playing with shaderoptions a little bit but it didn't seem to be very useful -- afaik the compiler treated the shaderoptions like an additional input rather than actual options (and just errored out).
I was able to get debug symbols. I'll update my post with the code.
I was able to figure out how to add compiler options with shaderoptions I tested it with the /WX compiler flag (Because I wanted warnings as errors). And oddly enough premakes "FatalWarnings" flag didn't enable it.
I had a bug in my script. It was never actually generating cso files I was using old cso files. I updated the post again.
|
-1

All the shader* commands in Premake5 (current version 5.0.0-beta7) are scoped to the project (per-configuration). They do not work at file-scope (per-configuration) and no amount of file filtering can change that.

Shader files participate in a build by adding them to your Visual Studio project. You achieve that in Premake5 by adding them to your project's files{} table.

That's all you need to do to enable the HLSL Compiler properties in Visual Studio. It won't appear at all if there are no shaders participating in the build.

In Premake5, HLSL files in the files {} table are automatically tagged "FxCompile", but they will not compile until you set the appropriate shader types.


Solution

First, prefix encode the shader types in the shader file names:

  • cs_*.hlsl = Compute Shader

  • ds*.hlsl = Domain Shader

  • gs_*.hlsl = Geometry Shader

  • hs_*.hlsl = Hull Shader

  • ps_*.hlsl = Pixel Shader

  • vs_*.hlsl = Vertex Shader

Next, add all shaders to your premake5.lua files{} table.

Next, give all your include shaders a .hlsli extension. This automatically excludes them from the build (you'll have to view all files to see/edit them).

Next, add a helper function to your project's override.lua file:

local function get_shader_type(filename)
 local shader_type = nil
 if string.startswith(filename, "cs_") then
  shader_type = "Compute"
 elseif string.startswith(filename, "ds_") then
  shader_type = "Domain"
 elseif string.startswith(filename, "gs_") then
  shader_type = "Geometry"
 elseif string.startswith(filename, "hs_") then
  shader_type = "Hull"
 elseif string.startswith(filename, "ps_") then
  shader_type = "Pixel"
 elseif string.startswith(filename, "vs_") then
  shader_type = "Vertex"
 end
 return shader_type
end

Finally, override the emitFiles() function (the base function can be found in Premake5's vs2010_vcxproj.lua).

require('vstudio')

local p = premake

local project = p.project
local fileconfig = p.fileconfig
local dotnetbase = p.vstudio.dotnetbase

local m = p.vstudio.vc2010

p.override(m, "emitFiles", function(base, prj, group, tag, fileFunc, fileCfgFunc, checkFunc)
 local files = group.files
 if files and #files > 0 then
  p.push('<ItemGroup>')
  for _, file in ipairs(files) do
   local fcfg = nil

   local contents = p.capture(function ()
    p.push()
    p.callArray(fileFunc, nil, file)
    m.conditionalElements = {}
    for cfg in project.eachconfig(prj) do
     fcfg = fileconfig.getconfig(file, cfg)
     if not checkFunc or checkFunc(cfg, fcfg) then
      if tag == "FxCompile" then
       shader_type = get_shader_type(path.getbasename(file.name))
       if shader_type then
        condition = string.gsub(dotnetbase.condition(cfg), "%s+", "")
        p.w("<ShaderType "..condition..">"..shader_type.."</ShaderType>")
       end
      else
       p.callArray(fileCfgFunc, fcfg, m.configPair(cfg))
      end
     end
    end
    if #m.conditionalElements > 0 then
     m.emitConditionalElements(prj)
    end
    p.pop()
   end)

   local rel = path.translate(file.relpath)

   -- SharedItems projects paths are prefixed with a magical variable
   if prj.kind == p.SHAREDITEMS then
    rel = "$(MSBuildThisFileDirectory)" .. rel
   end

   if #contents > 0 then
    p.push('<%s Include="%s">', tag, rel)
    p.outln(contents)
    p.pop('</%s>', tag)
   else
    p.x('<%s Include="%s" />', tag, rel)
   end

  end
  p.pop('</ItemGroup>')
 end
end)

With all that done, include the override.lua in your project's premake5.lua.


More Information

The bulk of the override function is just a duplicate of the base function. The base function creates an <ItemGroup> block in your vcxproj file based upon the tag parameter. For a file tagged "FxCompile", no per-configuration contents are captured, so the function emits a single line <FxCompile> tag in the Visual Studio project which simply states the file participates in all build configurations. But that file will fail to compile because no shader type attributes are attached to the tag. The override adds these missing attributes.

The only change from the base implementation is from this:

p.callArray(fileCfgFunc, fcfg, m.configPair(cfg))

To this:

if tag == "FxCompile" then
 shader_type = get_shader_type(path.getbasename(file.name))
 if shader_type then
  condition = string.gsub(dotnetbase.condition(cfg), "%s+", "")
  p.w("<ShaderType "..condition..">"..shader_type.."</ShaderType>")
 end
else
 p.callArray(fileCfgFunc, fcfg, m.configPair(cfg))
end

In essence, when the tag is "FxCompile", the call to p.callArray(fileCfgFunc, fcfg, m.configPair(cfg)) will not generate any contents because there are none to generate. So, rather than calling a function that returns no content, we generate the required per-configuration contents ourselves, by determining the shader type from the file name prefix, constructing the required condition, and writing to contents. Those writes will be captured by the contents variable which will be written later in the function, once all the configurations for the file have been captured.

Including the override and adding some HLSL files to your project is all you need to do. No need to filter shader files or call buildaction("FxCompile"); that's all done automatically as soon as you add a shader file to your premake5.lua.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.